Decompiled source of Wildroot v0.1.21

plugins/Wildroot/Wildroot.Core.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ExtraPotions.Meadworks.Contracts;
using ExtraPotions.Meadworks.Crucible;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ExtraPotions")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.21.0")]
[assembly: AssemblyInformationalVersion("0.1.21+fatal: not a git repository (or any of the parent directories): .git")]
[assembly: AssemblyProduct("Wildroot.Core")]
[assembly: AssemblyTitle("Wildroot.Core")]
[assembly: AssemblyVersion("0.1.21.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 ExtraPotions.Wildroot
{
	public enum ExtraResourceOrigin
	{
		None,
		LocalFile,
		Server
	}
	public enum CatalogMatch
	{
		Unknown,
		Same,
		Different
	}
	public sealed class CatalogMismatchGate
	{
		public const double SettleSeconds = 10.0;

		private string pending = "";

		private string warned = "";

		private double since;

		public bool Pending
		{
			get
			{
				if (pending.Length > 0)
				{
					return !string.Equals(pending, warned, StringComparison.Ordinal);
				}
				return false;
			}
		}

		public double Since => since;

		public string? Observe(CatalogMatch match, string? server, string? local, double now)
		{
			if (match != CatalogMatch.Different)
			{
				pending = "";
				warned = "";
				return null;
			}
			string text = server + "/" + local;
			if (!string.Equals(text, pending, StringComparison.Ordinal))
			{
				pending = text;
				since = now;
				return null;
			}
			if (now - since < 10.0 || string.Equals(text, warned, StringComparison.Ordinal))
			{
				return null;
			}
			warned = text;
			return text;
		}
	}
	public static class CatalogDistribution
	{
		public const int MaxSharedLength = 65536;

		public static ExtraResourceOrigin Origin(SessionRole role, bool enabled)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Invalid comparison between Unknown and I4
			if (enabled)
			{
				if ((int)role != 3)
				{
					return ExtraResourceOrigin.LocalFile;
				}
				return ExtraResourceOrigin.Server;
			}
			return ExtraResourceOrigin.None;
		}

		public static bool Publishes(SessionRole role)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)role != 2)
			{
				return (int)role == 4;
			}
			return true;
		}

		public static string? Shareable(string? text)
		{
			if (text == null)
			{
				text = "";
			}
			if (text.Length <= 65536)
			{
				return text;
			}
			return null;
		}

		public static string Fingerprint(IEnumerable<PlantDefinition> definitions)
		{
			if (definitions == null)
			{
				throw new ArgumentNullException("definitions");
			}
			IOrderedEnumerable<string> values = definitions.Select(Line).OrderBy<string, string>((string l) => l, StringComparer.Ordinal);
			using SHA256 sHA = SHA256.Create();
			byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(string.Join("\n", values)));
			StringBuilder stringBuilder = new StringBuilder(16);
			for (int num = 0; num < 8; num++)
			{
				stringBuilder.Append(array[num].ToString("x2", CultureInfo.InvariantCulture));
			}
			return stringBuilder.ToString();
		}

		public static CatalogMatch Compare(string? server, string? local)
		{
			if (!string.IsNullOrEmpty(server) && !string.IsNullOrEmpty(local))
			{
				if (!string.Equals(server, local, StringComparison.Ordinal))
				{
					return CatalogMatch.Different;
				}
				return CatalogMatch.Same;
			}
			return CatalogMatch.Unknown;
		}

		private static string Line(PlantDefinition d)
		{
			return string.Join("|", d.Id, d.Prefab, d.GrownPrefab, d.Kind.ToString(), string.Join(",", d.Costs.Select((ResourceCost c) => c.ItemId + ":" + c.Amount.ToString(CultureInfo.InvariantCulture))), d.HarvestItem, d.Yield.ToString(CultureInfo.InvariantCulture), N(d.GrowMin), N(d.GrowMax), N(d.RespawnSeconds), N(d.Radius), N(d.ScaleMin), N(d.ScaleMax), d.Enabled ? "1" : "0", d.Free ? "1" : "0");
			static string N(double v)
			{
				return v.ToString("R", CultureInfo.InvariantCulture);
			}
		}
	}
	public sealed class RecordedValue
	{
		public object Handle { get; }

		public string Member { get; }

		public object? Original { get; }

		internal RecordedValue(object handle, string member, object? original)
		{
			Handle = handle;
			Member = member;
			Original = original;
		}
	}
	public sealed class MutationLedger
	{
		private sealed class Key : IEquatable<Key>
		{
			private readonly object handle;

			private readonly string member;

			public Key(object handle, string member)
			{
				this.handle = handle;
				this.member = member;
			}

			public bool Equals(Key? other)
			{
				if (other != null && handle == other.handle)
				{
					return string.Equals(member, other.member, StringComparison.Ordinal);
				}
				return false;
			}

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

			public override int GetHashCode()
			{
				return RuntimeHelpers.GetHashCode(handle) * 31 + StringComparer.Ordinal.GetHashCode(member);
			}
		}

		private readonly Dictionary<Key, RecordedValue> index = new Dictionary<Key, RecordedValue>();

		private readonly List<RecordedValue> order = new List<RecordedValue>();

		public int Count => order.Count;

		public bool Capture(object handle, string member, object? original)
		{
			if (handle == null)
			{
				throw new ArgumentNullException("handle");
			}
			if (string.IsNullOrEmpty(member))
			{
				throw new ArgumentException("Expected a member name.", "member");
			}
			Key key = new Key(handle, member);
			if (index.ContainsKey(key))
			{
				return false;
			}
			RecordedValue recordedValue = new RecordedValue(handle, member, original);
			index.Add(key, recordedValue);
			order.Add(recordedValue);
			return true;
		}

		public RecordedValue? Original(object handle, string member)
		{
			if (handle == null || member == null || !index.TryGetValue(new Key(handle, member), out RecordedValue value))
			{
				return null;
			}
			return value;
		}

		public IReadOnlyList<RecordedValue> Restore()
		{
			List<RecordedValue> list = order.AsEnumerable().Reverse().ToList();
			order.Clear();
			index.Clear();
			return list.AsReadOnly();
		}
	}
	public static class CultivatorMembership
	{
		public static IReadOnlyList<string> Restore(IEnumerable<string> current, IEnumerable<string> native, IEnumerable<string> managed)
		{
			HashSet<string> hashSet = new HashSet<string>(native ?? throw new ArgumentNullException("native"), StringComparer.Ordinal);
			HashSet<string> hashSet2 = new HashSet<string>(managed ?? throw new ArgumentNullException("managed"), StringComparer.Ordinal);
			List<string> list = new List<string>();
			foreach (string item in current ?? throw new ArgumentNullException("current"))
			{
				if ((!hashSet2.Contains(item) || hashSet.Contains(item)) && !list.Contains(item))
				{
					list.Add(item);
				}
			}
			foreach (string item2 in native)
			{
				if (!list.Contains(item2))
				{
					list.Add(item2);
				}
			}
			return list.AsReadOnly();
		}
	}
	public static class CatalogLoader
	{
		private static string Text(JObject value, string key, string fallback = "")
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			JToken val = value[key];
			if (val == null)
			{
				return fallback;
			}
			if ((int)val.Type != 8)
			{
				throw new ArgumentException("Expected text for " + key);
			}
			return Extensions.Value<string>((IEnumerable<JToken>)val) ?? fallback;
		}

		private static double Number(JObject value, string key, double fallback)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Invalid comparison between Unknown and I4
			JToken val = value[key];
			if (val == null)
			{
				return fallback;
			}
			if ((int)val.Type != 7 && (int)val.Type != 6)
			{
				throw new ArgumentException("Expected number for " + key);
			}
			double num = Extensions.Value<double>((IEnumerable<JToken>)val);
			if (double.IsNaN(num) || double.IsInfinity(num))
			{
				throw new ArgumentException("Nonfinite " + key);
			}
			return num;
		}

		private static int Integer(JObject value, string key, int fallback)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			JToken val = value[key];
			if (val == null)
			{
				return fallback;
			}
			if ((int)val.Type != 6)
			{
				throw new ArgumentException("Expected integer for " + key);
			}
			return Extensions.Value<int>((IEnumerable<JToken>)val);
		}

		private static bool Flag(JObject value, string key, bool fallback)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Invalid comparison between Unknown and I4
			JToken val = value[key];
			if (val == null)
			{
				return fallback;
			}
			if ((int)val.Type != 9)
			{
				throw new ArgumentException("Expected boolean for " + key);
			}
			return Extensions.Value<bool>((IEnumerable<JToken>)val);
		}

		public static CatalogLoadResult Load(string json, IPrefabCatalog prefabs)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//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_0078: Expected O, but got Unknown
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Expected O, but got Unknown
			if (prefabs == null)
			{
				throw new ArgumentNullException("prefabs");
			}
			List<string> list = new List<string>();
			List<PlantDefinition> list2 = new List<PlantDefinition>();
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			try
			{
				if (json == null || json.Length > 1048576)
				{
					throw new ArgumentException("Catalog size exceeded");
				}
				JObject val2;
				using (StringReader stringReader = new StringReader(json))
				{
					JsonTextReader val = new JsonTextReader((TextReader)stringReader)
					{
						MaxDepth = 32,
						FloatParseHandling = (FloatParseHandling)1
					};
					try
					{
						val2 = JObject.Load((JsonReader)(object)val, new JsonLoadSettings
						{
							DuplicatePropertyNameHandling = (DuplicatePropertyNameHandling)2
						});
						if (((JsonReader)val).Read())
						{
							throw new ArgumentException("Trailing catalog content");
						}
					}
					finally
					{
						((IDisposable)val)?.Dispose();
					}
				}
				if (Integer(val2, "schema", -1) != 1)
				{
					throw new ArgumentException("Unsupported catalog schema");
				}
				JToken obj = val2["entries"];
				JArray val3 = (JArray)(object)((obj is JArray) ? obj : null);
				if (val3 == null || ((JContainer)val3).Count > 4096)
				{
					throw new ArgumentException("Invalid catalog entry count");
				}
				foreach (JToken item in val3)
				{
					try
					{
						JObject val4 = (JObject)(object)((item is JObject) ? item : null);
						if (val4 == null)
						{
							throw new ArgumentException("Entry must be an object");
						}
						string text = Text(val4, "id");
						string prefab = Text(val4, "prefab");
						string text2 = Text(val4, "grownPrefab");
						string text3 = Text(val4, "harvestItem");
						if (string.IsNullOrWhiteSpace(text) || text.Length > 128 || !hashSet.Add(text))
						{
							throw new ArgumentException("Missing, oversized or duplicate id");
						}
						if (!prefabs.HasPrefab(prefab) || (!string.IsNullOrEmpty(text2) && !prefabs.HasPrefab(text2)))
						{
							throw new ArgumentException("Unknown prefab");
						}
						if (!Enum.TryParse<PlantKind>(Text(val4, "kind"), ignoreCase: false, out var result) || !Enum.IsDefined(typeof(PlantKind), result))
						{
							throw new ArgumentException("Unknown plant kind");
						}
						bool flag = Flag(val4, "enabled", fallback: true);
						bool flag2 = Flag(val4, "free", fallback: false);
						List<ResourceCost> list3 = new List<ResourceCost>();
						JToken obj2 = val4["costs"];
						foreach (JToken item2 in (JArray)(((obj2 is JArray) ? obj2 : null) ?? throw new ArgumentException("Costs must be explicit")))
						{
							JObject val5 = (JObject)(object)((item2 is JObject) ? item2 : null);
							if (val5 == null)
							{
								throw new ArgumentException("Invalid cost");
							}
							string text4 = Text(val5, "item");
							if (!prefabs.HasItem(text4))
							{
								throw new ArgumentException("Unknown material");
							}
							list3.Add(new ResourceCost(text4, Integer(val5, "amount", 0)));
						}
						if (flag && list3.Count == 0 && !flag2)
						{
							throw new ArgumentException("No implicit free planting");
						}
						int num = Integer(val4, "yield", 0);
						if (num < 0 || (!string.IsNullOrEmpty(text3) && !prefabs.HasItem(text3)) || (num > 0 && string.IsNullOrEmpty(text3)))
						{
							throw new ArgumentException("Invalid harvest");
						}
						double num2 = Number(val4, "growMin", 0.0);
						double num3 = Number(val4, "growMax", num2);
						double num4 = Number(val4, "respawnSeconds", 0.0);
						double num5 = Number(val4, "radius", 1.0);
						double num6 = Number(val4, "scaleMin", 1.0);
						double num7 = Number(val4, "scaleMax", num6);
						if (num2 < 0.0 || num3 < num2 || num4 < 0.0 || num5 < 0.0 || num6 <= 0.0 || num7 < num6)
						{
							throw new ArgumentException("Invalid growth, scale or spacing range");
						}
						list2.Add(new PlantDefinition(text, prefab, text2, result, list3, text3, num, num2, num3, num4, num5, num6, num7, flag, flag2));
					}
					catch (Exception ex) when (ex is ArgumentException || ex is OverflowException || ex is FormatException)
					{
						list.Add("Entry " + list2.Count + ": " + ex.Message);
					}
				}
			}
			catch (Exception ex2) when (ex2 is JsonException || ex2 is ArgumentException || ex2 is OverflowException || ex2 is FormatException)
			{
				list.Add(ex2.Message);
			}
			return new CatalogLoadResult((list.Count == 0) ? new CatalogSnapshot(list2) : null, list);
		}
	}
	public enum PlantKind
	{
		Crop,
		Sapling,
		Pickable,
		Decorative,
		Vine
	}
	public sealed class PlantDefinition
	{
		public string Id { get; }

		public string Prefab { get; }

		public string GrownPrefab { get; }

		public PlantKind Kind { get; }

		public IReadOnlyList<ResourceCost> Costs { get; }

		public string HarvestItem { get; }

		public int Yield { get; }

		public double GrowMin { get; }

		public double GrowMax { get; }

		public double RespawnSeconds { get; }

		public double Radius { get; }

		public double ScaleMin { get; }

		public double ScaleMax { get; }

		public bool Enabled { get; }

		public bool Free { get; }

		public PlantDefinition(string id, string prefab, string grownPrefab, PlantKind kind, IReadOnlyList<ResourceCost> costs, string harvestItem, int yield, double growMin, double growMax, double respawnSeconds, double radius, double scaleMin, double scaleMax, bool enabled, bool free)
		{
			Id = id;
			Prefab = prefab;
			GrownPrefab = grownPrefab;
			Kind = kind;
			Costs = new List<ResourceCost>(costs).AsReadOnly();
			HarvestItem = harvestItem;
			Yield = yield;
			GrowMin = growMin;
			GrowMax = growMax;
			RespawnSeconds = respawnSeconds;
			Radius = radius;
			ScaleMin = scaleMin;
			ScaleMax = scaleMax;
			Enabled = enabled;
			Free = free;
		}
	}
	public interface IPrefabCatalog
	{
		bool HasPrefab(string prefab);

		bool HasItem(string item);
	}
	public sealed class CatalogSnapshot
	{
		public IReadOnlyList<PlantDefinition> Entries { get; }

		public CatalogSnapshot(IReadOnlyList<PlantDefinition> entries)
		{
			Entries = new List<PlantDefinition>(entries).AsReadOnly();
		}
	}
	public sealed class CatalogLoadResult
	{
		public CatalogSnapshot? Snapshot { get; }

		public IReadOnlyList<string> Errors { get; }

		public bool Valid
		{
			get
			{
				if (Snapshot != null)
				{
					return Errors.Count == 0;
				}
				return false;
			}
		}

		public CatalogLoadResult(CatalogSnapshot? snapshot, IReadOnlyList<string> errors)
		{
			Snapshot = snapshot;
			Errors = new List<string>(errors).AsReadOnly();
		}
	}
	public sealed class CatalogStore
	{
		public CatalogSnapshot Current { get; private set; }

		public CatalogStore(CatalogSnapshot initial)
		{
			Current = initial ?? throw new ArgumentNullException("initial");
		}

		public bool TryReplace(CatalogLoadResult candidate)
		{
			if (candidate == null || !candidate.Valid)
			{
				return false;
			}
			Current = candidate.Snapshot;
			return true;
		}
	}
	public readonly struct PlantState
	{
		public double Started { get; }

		public double Duration { get; }

		public bool Picked { get; }

		public bool Blocked { get; }

		public PlantState(double started, double duration, bool picked, bool blocked)
		{
			Started = started;
			Duration = duration;
			Picked = picked;
			Blocked = blocked;
		}
	}
	public enum GrowthPhase
	{
		Growing,
		Ready,
		Regrowing,
		Blocked,
		Exhausted
	}
	public readonly struct GrowthResult
	{
		public GrowthPhase Phase { get; }

		public double Fraction { get; }

		public double Remaining { get; }

		public GrowthResult(GrowthPhase phase, double fraction, double remaining)
		{
			Phase = phase;
			Fraction = fraction;
			Remaining = remaining;
		}
	}
	public interface ICatalogSettings
	{
		bool TryGet<T>(string id, out T value);
	}
	public sealed class CatalogSettings : ICatalogSettings
	{
		private readonly Dictionary<string, object> values;

		public CatalogSettings(IDictionary<string, object>? values = null)
		{
			this.values = new Dictionary<string, object>(values ?? new Dictionary<string, object>(), StringComparer.Ordinal);
		}

		public CatalogSettings With(string id, object value)
		{
			CatalogSettings catalogSettings = new CatalogSettings(values);
			catalogSettings.values[id] = value;
			return catalogSettings;
		}

		public bool TryGet<T>(string id, out T value)
		{
			if (values.TryGetValue(id, out object value2) && value2 is T val)
			{
				value = val;
				return true;
			}
			value = default(T);
			return false;
		}
	}
	public enum CatalogSection
	{
		None,
		Berries,
		Mushrooms,
		Flowers,
		Debris,
		Crops,
		Saplings,
		Vines,
		MiscFlora
	}
	public readonly struct GrowthOverride
	{
		public double GrowMin { get; }

		public double GrowMax { get; }

		public double Radius { get; }

		public double ScaleMin { get; }

		public double ScaleMax { get; }

		public GrowthOverride(double growMin, double growMax, double radius, double scaleMin, double scaleMax)
		{
			GrowMin = growMin;
			GrowMax = growMax;
			Radius = radius;
			ScaleMin = scaleMin;
			ScaleMax = scaleMax;
		}
	}
	public static class CatalogPolicy
	{
		public const double MinimumSaplingGrowth = 10.0;

		private static readonly Dictionary<string, (CatalogSection section, string stem)> Stems = new Dictionary<string, (CatalogSection, string)>(StringComparer.Ordinal)
		{
			{
				"RaspberryBush",
				(CatalogSection.Berries, "Raspberry")
			},
			{
				"BlueberryBush",
				(CatalogSection.Berries, "Blueberry")
			},
			{
				"CloudberryBush",
				(CatalogSection.Berries, "Cloudberry")
			},
			{
				"LingonberryBush",
				(CatalogSection.Berries, "Lingonberry")
			},
			{
				"Pickable_Mushroom",
				(CatalogSection.Mushrooms, "Mushroom")
			},
			{
				"Pickable_Mushroom_yellow",
				(CatalogSection.Mushrooms, "YellowMushroom")
			},
			{
				"Pickable_Mushroom_blue",
				(CatalogSection.Mushrooms, "BlueMushroom")
			},
			{
				"Pickable_SmokePuff",
				(CatalogSection.Mushrooms, "Smokepuff")
			},
			{
				"Pickable_Thistle",
				(CatalogSection.Flowers, "Thistle")
			},
			{
				"Pickable_Dandelion",
				(CatalogSection.Flowers, "Dandelion")
			},
			{
				"Pickable_Fiddlehead",
				(CatalogSection.Flowers, "Fiddlehead")
			},
			{
				"Pickable_Branch",
				(CatalogSection.Debris, "PickableBranch")
			},
			{
				"Pickable_Stone",
				(CatalogSection.Debris, "PickableStone")
			},
			{
				"Pickable_Flint",
				(CatalogSection.Debris, "PickableFlint")
			},
			{
				"sapling_barley",
				(CatalogSection.Crops, "Barley")
			},
			{
				"sapling_carrot",
				(CatalogSection.Crops, "Carrot")
			},
			{
				"sapling_flax",
				(CatalogSection.Crops, "Flax")
			},
			{
				"sapling_onion",
				(CatalogSection.Crops, "Onion")
			},
			{
				"sapling_seedcarrot",
				(CatalogSection.Crops, "SeedCarrot")
			},
			{
				"sapling_seedonion",
				(CatalogSection.Crops, "SeedOnion")
			},
			{
				"sapling_seedturnip",
				(CatalogSection.Crops, "SeedTurnip")
			},
			{
				"sapling_turnip",
				(CatalogSection.Crops, "Turnip")
			},
			{
				"sapling_magecap",
				(CatalogSection.Crops, "Magecap")
			},
			{
				"sapling_jotunpuffs",
				(CatalogSection.Crops, "JotunPuffs")
			},
			{
				"sapling_oat",
				(CatalogSection.Crops, "Oat")
			},
			{
				"sapling_poteitr",
				(CatalogSection.Crops, "Poteitr")
			},
			{
				"sapling_Kale",
				(CatalogSection.Crops, "Kale")
			},
			{
				"sapling_seedkale",
				(CatalogSection.Crops, "SeedKale")
			},
			{
				"Beech_Sapling",
				(CatalogSection.Saplings, "Beech")
			},
			{
				"Birch_Sapling",
				(CatalogSection.Saplings, "Birch")
			},
			{
				"Autumn_Birch_Sapling",
				(CatalogSection.Saplings, "AutumnBirch")
			},
			{
				"Ancient_Sapling",
				(CatalogSection.Saplings, "Ancient")
			},
			{
				"Ygga_Sapling",
				(CatalogSection.Saplings, "Ygga")
			},
			{
				"Ashwood_Sapling",
				(CatalogSection.Saplings, "Ashwood")
			},
			{
				"Oak_Sapling",
				(CatalogSection.Saplings, "Oak")
			},
			{
				"FirTree_Sapling",
				(CatalogSection.Saplings, "Fir")
			},
			{
				"FirTree_big_Sapling",
				(CatalogSection.Saplings, "Timberwood")
			},
			{
				"PineTree_Sapling",
				(CatalogSection.Saplings, "Pine")
			}
		};

		public const string AshlandsCropsId = "Restrictions.AshlandsCrops";

		private static readonly Point3[] VineSnaps = new Point3[4]
		{
			new Point3(1.0, -0.5, 0.0),
			new Point3(-1.0, -0.5, 0.0),
			new Point3(1.0, -2.0, 0.0),
			new Point3(-1.0, -2.0, 0.0)
		};

		public static readonly IReadOnlyCollection<string> PickedSpawners = new HashSet<string>(StringComparer.Ordinal) { "Pickable_Mushroom", "Pickable_Mushroom_yellow", "Pickable_Mushroom_blue", "Pickable_SmokePuff", "Pickable_Thistle", "Pickable_Dandelion", "Pickable_Fiddlehead" };

		public static CatalogSection Section(PlantDefinition definition)
		{
			if (definition == null)
			{
				throw new ArgumentNullException("definition");
			}
			if (Stems.TryGetValue(definition.Id, out (CatalogSection, string) value))
			{
				return value.Item1;
			}
			return definition.Kind switch
			{
				PlantKind.Vine => CatalogSection.Vines, 
				PlantKind.Decorative => CatalogSection.MiscFlora, 
				PlantKind.Sapling => CatalogSection.Saplings, 
				PlantKind.Crop => CatalogSection.Crops, 
				_ => CatalogSection.None, 
			};
		}

		public static string Stem(PlantDefinition definition)
		{
			if (definition == null || !Stems.TryGetValue(definition.Id, out (CatalogSection, string) value))
			{
				return "";
			}
			return value.Item2;
		}

		private static bool Flag(ICatalogSettings s, string id, bool fallback)
		{
			if (!s.TryGet<bool>(id, out var value))
			{
				return fallback;
			}
			return value;
		}

		private static double Number(ICatalogSettings s, string id, double fallback, IList<string>? warnings, double min = 0.0)
		{
			double num;
			int value2;
			if (s.TryGet<float>(id, out var value))
			{
				num = value;
			}
			else if (s.TryGet<int>(id, out value2))
			{
				num = value2;
			}
			else
			{
				if (!s.TryGet<double>(id, out var value3))
				{
					return fallback;
				}
				num = value3;
			}
			if (double.IsNaN(num) || double.IsInfinity(num) || num < min)
			{
				warnings?.Add(id + " out of range; kept " + fallback.ToString(CultureInfo.InvariantCulture));
				return fallback;
			}
			return num;
		}

		public static ISet<string> DisabledNames(ICatalogSettings s)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			if (!s.TryGet<string>("Catalog.DisabledResourceNames", out var value) || string.IsNullOrWhiteSpace(value))
			{
				return hashSet;
			}
			string[] array = value.Split(',');
			foreach (string text in array)
			{
				if (text.Trim().Length > 0)
				{
					hashSet.Add(text.Trim());
				}
			}
			return hashSet;
		}

		public static bool CropOverrides(ICatalogSettings s)
		{
			return Flag(s, "Growth.Crops.EnableCropOverrides", fallback: false);
		}

		public static bool VineOverrides(ICatalogSettings s)
		{
			return Flag(s, "Growth.Vines.EnableVineOverrides", fallback: false);
		}

		public static double? VineAttachDistance(ICatalogSettings s)
		{
			if (!VineOverrides(s))
			{
				return null;
			}
			return Number(s, "Growth.Vines.VineAttachDistance", 1.8, null);
		}

		public static GrowthOverride? ModdedCrop(ICatalogSettings s)
		{
			if (!CropOverrides(s) || !Flag(s, "Growth.Crops.OverrideModdedCrops", fallback: true))
			{
				return null;
			}
			return CropGrowth(s, new GrowthOverride(4000.0, 5000.0, 0.5, 0.9, 1.1), null);
		}

		public static GrowthOverride? ModdedSapling(ICatalogSettings s)
		{
			if (!Flag(s, "Growth.Saplings.OverrideModdedSaplings", fallback: false))
			{
				return null;
			}
			double num = Math.Max(10.0, Number(s, "Growth.Saplings.ModdedSaplingGrowthTime", 3000.0, null));
			double num2 = Number(s, "Growth.Saplings.ModdedSaplingMinScale", 0.5, null, 1E-06);
			return new GrowthOverride(scaleMax: Math.Max(num2, Number(s, "Growth.Saplings.ModdedSaplingMaxScale", 2.0, null, 1E-06)), growMin: num, growMax: num, radius: Number(s, "Growth.Saplings.ModdedSaplingGrowRadius", 2.0, null), scaleMin: num2);
		}

		private static GrowthOverride CropGrowth(ICatalogSettings s, GrowthOverride current, IList<string>? warnings)
		{
			double num = Number(s, "Growth.Crops.CropGrowTimeMin", current.GrowMin, warnings);
			double growMax = Math.Max(num, Number(s, "Growth.Crops.CropGrowTimeMax", current.GrowMax, warnings));
			double num2 = Number(s, "Growth.Crops.CropMinScale", current.ScaleMin, warnings, 1E-06);
			return new GrowthOverride(scaleMax: Math.Max(num2, Number(s, "Growth.Crops.CropMaxScale", current.ScaleMax, warnings, 1E-06)), growMin: num, growMax: growMax, radius: Number(s, "Growth.Crops.CropGrowRadius", current.Radius, warnings), scaleMin: num2);
		}

		public static CatalogSnapshot Apply(CatalogSnapshot baseline, ICatalogSettings settings, IList<string>? warnings = null)
		{
			if (baseline == null || settings == null)
			{
				throw new ArgumentNullException("baseline");
			}
			ISet<string> disabled = DisabledNames(settings);
			bool misc = Flag(settings, "Catalog.EnableMiscFlora", fallback: true);
			bool debris = Flag(settings, "Catalog.EnableDebris", fallback: true);
			bool crops = CropOverrides(settings);
			bool vines = VineOverrides(settings);
			return new CatalogSnapshot(baseline.Entries.Select((PlantDefinition d) => Effective(d, settings, disabled, misc, debris, crops, vines, warnings)).ToList());
		}

		private static PlantDefinition Effective(PlantDefinition d, ICatalogSettings s, ISet<string> disabled, bool misc, bool debris, bool crops, bool vines, IList<string>? warnings)
		{
			CatalogSection catalogSection = Section(d);
			string text = Stem(d);
			IReadOnlyList<ResourceCost> costs = d.Costs;
			int num = d.Yield;
			double num2 = d.RespawnSeconds;
			double num3 = d.GrowMin;
			double growMax = d.GrowMax;
			double num4 = d.Radius;
			double num5 = d.ScaleMin;
			double num6 = d.ScaleMax;
			bool enabled = d.Enabled;
			object obj;
			string text2;
			switch (catalogSection)
			{
			case CatalogSection.Berries:
			case CatalogSection.Mushrooms:
			case CatalogSection.Flowers:
				obj = "Catalog.Vanilla." + catalogSection.ToString() + ".";
				goto IL_00a8;
			case CatalogSection.Debris:
				obj = "Catalog.Vanilla.Debris.";
				goto IL_00a8;
			case CatalogSection.Crops:
				if (crops && text.Length > 0)
				{
					costs = Cost(costs, Number(s, "Catalog.Vanilla.Crops." + text + "Cost", FirstAmount(costs), warnings), ref enabled);
					num = (int)Number(s, "Catalog.Vanilla.Crops." + text + "Return", num, warnings);
					GrowthOverride growthOverride = CropGrowth(s, new GrowthOverride(num3, growMax, num4, num5, num6), warnings);
					num3 = growthOverride.GrowMin;
					growMax = growthOverride.GrowMax;
					num4 = growthOverride.Radius;
					num5 = growthOverride.ScaleMin;
					num6 = growthOverride.ScaleMax;
				}
				break;
			case CatalogSection.Saplings:
				if (text.Length > 0)
				{
					num3 = (growMax = Math.Max(10.0, Number(s, "Growth.Saplings." + text + "GrowthTime", num3, warnings)));
					num4 = Number(s, "Growth.Saplings." + text + "GrowRadius", num4, warnings);
					num5 = Number(s, "Growth.Saplings." + text + "MinScale", num5, warnings, 1E-06);
					num6 = Math.Max(num5, Number(s, "Growth.Saplings." + text + "MaxScale", num6, warnings, 1E-06));
				}
				break;
			case CatalogSection.Vines:
				if (vines)
				{
					num3 = (growMax = Number(s, "Growth.Vines.VineGrowthTime", num3, warnings));
					num4 = Number(s, "Growth.Vines.VineGrowRadius", num4, warnings);
					num2 = Number(s, "Growth.Vines.VineBerryRespawnTime", num2 / 60.0, warnings) * 60.0;
					num = (int)Number(s, "Growth.Vines.VineBerryReturn", num, warnings);
				}
				break;
			case CatalogSection.MiscFlora:
				{
					if (!misc)
					{
						enabled = false;
					}
					break;
				}
				IL_00a8:
				text2 = (string)obj;
				costs = Cost(costs, Number(s, text2 + text + "Cost", FirstAmount(costs), warnings), ref enabled);
				num = (int)Number(s, text2 + text + "Return", num, warnings);
				num2 = Number(s, text2 + text + "RespawnTime", num2 / 60.0, warnings) * 60.0;
				if (catalogSection == CatalogSection.Debris && !debris)
				{
					enabled = false;
				}
				break;
			}
			if (disabled.Contains(d.Prefab))
			{
				enabled = false;
			}
			return new PlantDefinition(d.Id, d.Prefab, d.GrownPrefab, d.Kind, costs, d.HarvestItem, num, num3, growMax, num2, num4, num5, num6, enabled, d.Free);
		}

		public static bool EnforcesBiome(PlantDefinition definition, bool nativePrefab, ICatalogSettings s)
		{
			if (definition == null || s == null)
			{
				throw new ArgumentNullException("definition");
			}
			if (definition.Kind != PlantKind.Crop && definition.Kind != PlantKind.Sapling && definition.Kind != PlantKind.Vine)
			{
				return false;
			}
			if (!nativePrefab)
			{
				return Flag(s, "Restrictions.EnforceBiomes", fallback: false);
			}
			return Flag(s, "Restrictions.EnforceBiomesVanilla", fallback: true);
		}

		public static bool AshlandsCrops(ICatalogSettings s)
		{
			return Flag(s ?? throw new ArgumentNullException("s"), "Restrictions.AshlandsCrops", fallback: true);
		}

		public static int? ReferenceBiome(string prefab, ICatalogSettings s)
		{
			return ReferenceTraits.Biome(prefab, AshlandsCrops(s));
		}

		public static CatalogSnapshot WithExtras(CatalogSnapshot effective, IReadOnlyList<ExtraResourceDefinition> extras, ICatalogSettings s, IList<string>? warnings = null)
		{
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Expected O, but got Unknown
			if (effective == null || extras == null || s == null)
			{
				throw new ArgumentNullException("effective");
			}
			ISet<string> set = DisabledNames(s);
			List<PlantDefinition> list = effective.Entries.ToList();
			HashSet<string> hashSet = new HashSet<string>(list.Select((PlantDefinition e) => e.Prefab), StringComparer.Ordinal);
			foreach (ExtraResourceDefinition extra in extras)
			{
				if (extra != null)
				{
					if (!hashSet.Add(extra.Prefab))
					{
						warnings?.Add("Extra resource " + extra.Prefab + " is already a catalog entry; skipped");
						continue;
					}
					list.Add(new PlantDefinition(extra.Prefab, extra.Prefab, "", PlantKind.Decorative, (IReadOnlyList<ResourceCost>)(object)new ResourceCost[1]
					{
						new ResourceCost(extra.Material, extra.Cost)
					}, "", 0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, !set.Contains(extra.Prefab), free: false));
				}
			}
			return new CatalogSnapshot(list);
		}

		public static IReadOnlyList<Point3> VineSnapPoints(PlantDefinition definition, ICatalogSettings s)
		{
			if (definition == null || s == null)
			{
				throw new ArgumentNullException("definition");
			}
			if (!(definition.Id == "vines") || !Flag(s, "Catalog.SnappableVines", fallback: true))
			{
				return Array.Empty<Point3>();
			}
			return VineSnaps;
		}

		public static bool ShowsPickedSpawner(string prefab, ICatalogSettings s)
		{
			if (s != null && prefab != null && PickedSpawners.Contains(prefab))
			{
				return Flag(s, "Catalog.ShowPickableSpawners", fallback: true);
			}
			return false;
		}

		private static double FirstAmount(IReadOnlyList<ResourceCost> costs)
		{
			return (costs.Count > 0) ? costs[0].Amount : 0;
		}

		private static IReadOnlyList<ResourceCost> Cost(IReadOnlyList<ResourceCost> costs, double amount, ref bool enabled)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			if (costs.Count == 0)
			{
				return costs;
			}
			int num = (int)Math.Min(2147483647.0, Math.Round(amount));
			if (num <= 0)
			{
				enabled = false;
				return costs;
			}
			List<ResourceCost> list = costs.ToList();
			list[0] = new ResourceCost(costs[0].ItemId, num);
			return list;
		}
	}
	public readonly struct PiecePolicy
	{
		public bool Targetable { get; }

		public bool Removable { get; }

		public bool RecoverResources { get; }

		public bool ToleratesEnvironment { get; }

		public bool GroundOnly { get; }

		public bool RequiresCultivation { get; }

		public bool RequiresSunlight { get; }

		public bool RequiresGrowthSpace { get; }

		public PiecePolicy(bool targetable, bool removable, bool recover, bool tolerates, bool groundOnly, bool cultivation, bool sunlight, bool growthSpace)
		{
			Targetable = targetable;
			Removable = removable;
			RecoverResources = recover;
			ToleratesEnvironment = tolerates;
			GroundOnly = groundOnly;
			RequiresCultivation = cultivation;
			RequiresSunlight = sunlight;
			RequiresGrowthSpace = growthSpace;
		}
	}
	public static class PiecePolicies
	{
		private static bool Flag(ICatalogSettings s, string id, bool fallback)
		{
			if (!s.TryGet<bool>(id, out var value))
			{
				return fallback;
			}
			return value;
		}

		public static PiecePolicy For(PlantDefinition definition, ICatalogSettings s)
		{
			if (definition == null || s == null)
			{
				throw new ArgumentNullException("definition");
			}
			CatalogSection catalogSection = CatalogPolicy.Section(definition);
			bool flag = CatalogPolicy.CropOverrides(s);
			bool flag2 = catalogSection == CatalogSection.Crops;
			bool flag3 = definition.Kind == PlantKind.Pickable;
			bool targetable = ((!flag2) ? Flag(s, "Restrictions.EnemiesTargetPieces", fallback: true) : (!flag || Flag(s, "Growth.Crops.EnemiesTargetCrops", fallback: true)));
			bool removable = catalogSection != CatalogSection.MiscFlora || Flag(s, "Restrictions.CanRemoveFlora", fallback: false);
			bool recover = (catalogSection == CatalogSection.Berries || catalogSection == CatalogSection.Mushrooms || catalogSection == CatalogSection.Flowers) && Flag(s, "Restrictions.RecoverResources", fallback: false);
			bool tolerates = !Flag(s, "Restrictions.PlantsRequireShielding", fallback: true);
			bool num = Flag(s, "Restrictions.PlaceAnywhere", fallback: false) && catalogSection != CatalogSection.Mushrooms && catalogSection != CatalogSection.Flowers && !flag2;
			bool flag4 = definition.Prefab == "FernAshlands" || definition.Prefab == "Pickable_Branch" || definition.Prefab == "Pickable_Stone" || definition.Prefab == "Pickable_Flint";
			bool groundOnly = !num || flag4;
			bool cultivation = ((!flag2) ? (flag3 && Flag(s, "Restrictions.RequireCultivation", fallback: false)) : (!flag || Flag(s, "Growth.Crops.CropsRequireCultivation", fallback: true)));
			bool sunlight = !num && (!flag2 || !flag || Flag(s, "Growth.Crops.CropsRequireSunlight", fallback: true));
			bool growthSpace = !num && (!flag2 || !flag || Flag(s, "Growth.Crops.CropsRequireGrowthSpace", fallback: true));
			return new PiecePolicy(targetable, removable, recover, tolerates, groundOnly, cultivation, sunlight, growthSpace);
		}

		public static bool SpawnsEmpty(PlantDefinition definition, ICatalogSettings s)
		{
			CatalogSection catalogSection = CatalogPolicy.Section(definition);
			if (catalogSection == CatalogSection.Berries || catalogSection == CatalogSection.Mushrooms || catalogSection == CatalogSection.Flowers || catalogSection == CatalogSection.Debris)
			{
				return Flag(s, "Restrictions.ResourcesSpawnEmpty", fallback: false);
			}
			return false;
		}
	}
	public readonly struct DropEntry
	{
		public string Item { get; }

		public int StackMin { get; }

		public int StackMax { get; }

		public DropEntry(string item, int stackMin, int stackMax)
		{
			Item = item;
			StackMin = stackMin;
			StackMax = stackMax;
		}
	}
	public sealed class DropTableValues
	{
		public int Min { get; }

		public int Max { get; }

		public double Chance { get; }

		public bool OneOfEach { get; }

		public IReadOnlyList<DropEntry> Entries { get; }

		public DropTableValues(int min, int max, double chance, bool oneOfEach, IReadOnlyList<DropEntry> entries)
		{
			Min = min;
			Max = max;
			Chance = chance;
			OneOfEach = oneOfEach;
			Entries = new List<DropEntry>(entries).AsReadOnly();
		}
	}
	public static class TreeDropPolicy
	{
		public static readonly IReadOnlyCollection<string> SeedItems = new HashSet<string>(StringComparer.Ordinal) { "BeechSeeds", "BirchSeeds", "Acorn", "PineCone", "FirCone", "FirConeFrost", "AncientSeed", "Sap" };

		public static DropTableValues Apply(DropTableValues original, ICatalogSettings s)
		{
			if (original == null || s == null)
			{
				throw new ArgumentNullException("original");
			}
			if (!(s.TryGet<bool>("Drops.EnableSeedOverrides", out var value) && value))
			{
				return original;
			}
			int num = Math.Max(0, Int("Drops.treeDropMin", 1));
			int max = Math.Max(num, Int("Drops.treeDropMax", 3));
			int seedMin = Math.Max(0, Int("Drops.seedDropMin", 1));
			int seedMax = Math.Max(seedMin, Int("Drops.seedDropMax", 2));
			float value2;
			double num2 = (s.TryGet<float>("Drops.dropChance", out value2) ? ((double)value2) : 0.5);
			if (double.IsNaN(num2))
			{
				num2 = 0.5;
			}
			bool value3;
			return new DropTableValues(chance: Math.Min(1.0, Math.Max(0.0, num2)), entries: original.Entries.Select((DropEntry e) => (!SeedItems.Contains(e.Item)) ? e : new DropEntry(e.Item, seedMin, seedMax)).ToList(), min: num, max: max, oneOfEach: s.TryGet<bool>("Drops.oneOfEach", out value3) && value3);
			int Int(string id, int fallback)
			{
				if (!s.TryGet<int>(id, out var value4))
				{
					return fallback;
				}
				return value4;
			}
		}
	}
	public static class HoverTimer
	{
		public static string Format(double remainingSeconds, double fraction, bool asPercentage)
		{
			if (double.IsNaN(remainingSeconds) || double.IsNaN(fraction))
			{
				throw new ArgumentException("Invalid growth timer");
			}
			if (remainingSeconds <= 0.0)
			{
				return "";
			}
			if (asPercentage)
			{
				return Math.Floor(Math.Min(1.0, Math.Max(0.0, fraction)) * 100.0).ToString(CultureInfo.InvariantCulture) + "%";
			}
			long num = (long)Math.Ceiling(remainingSeconds);
			long num2 = num / 3600;
			long num3 = num % 3600 / 60;
			long num4 = num % 60;
			if (num2 <= 0)
			{
				if (num3 <= 0)
				{
					return num4 + "s";
				}
				return num3 + "m " + num4 + "s";
			}
			return num2 + "h " + num3 + "m " + num4 + "s";
		}
	}
	public sealed class ExtraResourceDefinition
	{
		public string Prefab { get; }

		public string Material { get; }

		public int Cost { get; }

		public bool GroundOnly { get; }

		public string Name { get; }

		public string Description { get; }

		public ExtraResourceDefinition(string prefab, string material, int cost, bool groundOnly, string name, string description)
		{
			Prefab = prefab;
			Material = material;
			Cost = cost;
			GroundOnly = groundOnly;
			Name = name;
			Description = description;
		}
	}
	public sealed class ExtraResourceImportResult
	{
		public bool Valid { get; }

		public IReadOnlyList<ExtraResourceDefinition> Entries { get; }

		public IReadOnlyList<string> Warnings { get; }

		public ExtraResourceImportResult(bool valid, IReadOnlyList<ExtraResourceDefinition> entries, IReadOnlyList<string> warnings)
		{
			Valid = valid;
			Entries = new List<ExtraResourceDefinition>(entries).AsReadOnly();
			Warnings = new List<string>(warnings).AsReadOnly();
		}
	}
	public static class ExtraResourceImporter
	{
		public static ExtraResourceImportResult Parse(string text, IPrefabCatalog prefabs)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//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_0078: Expected O, but got Unknown
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Invalid comparison between Unknown and I4
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Invalid comparison between Unknown and I4
			if (prefabs == null)
			{
				throw new ArgumentNullException("prefabs");
			}
			List<ExtraResourceDefinition> list = new List<ExtraResourceDefinition>();
			List<string> list2 = new List<string>();
			try
			{
				if (text == null || text.Length > 1048576)
				{
					throw new ArgumentException("Extra resource size exceeded");
				}
				using (IEnumerator<string> enumerator = Blocks(text).GetEnumerator())
				{
					while (enumerator.MoveNext())
					{
						JsonTextReader val = new JsonTextReader((TextReader)new StringReader(enumerator.Current))
						{
							MaxDepth = 32
						};
						JObject val2;
						try
						{
							val2 = JObject.Load((JsonReader)(object)val, new JsonLoadSettings
							{
								DuplicatePropertyNameHandling = (DuplicatePropertyNameHandling)2
							});
						}
						finally
						{
							((IDisposable)val)?.Dispose();
						}
						try
						{
							string text2 = ReadText(val2, "prefabName");
							if (!text2.StartsWith("PE_Fake", StringComparison.Ordinal))
							{
								string text3 = ReadText(val2, "resourceName");
								JToken obj = val2["resourceCost"];
								if (obj == null || (int)obj.Type != 6)
								{
									throw new ArgumentException("Cost must be an integer");
								}
								int num = Extensions.Value<int>((IEnumerable<JToken>)val2["resourceCost"]);
								if (num <= 0 || !prefabs.HasPrefab(text2) || !prefabs.HasItem(text3))
								{
									throw new ArgumentException("Unresolved prefab/material or nonpositive cost");
								}
								JToken obj2 = val2["groundOnly"];
								if (obj2 == null || (int)obj2.Type != 9)
								{
									throw new ArgumentException("groundOnly must be boolean");
								}
								list.Add(new ExtraResourceDefinition(text2, text3, num, Extensions.Value<bool>((IEnumerable<JToken>)val2["groundOnly"]), ReadText(val2, "pieceName"), ReadText(val2, "pieceDescription")));
							}
						}
						catch (Exception ex) when (ex is ArgumentException || ex is OverflowException || ex is FormatException)
						{
							list2.Add("Skipped extra resource: " + ex.Message);
						}
					}
				}
				return new ExtraResourceImportResult(valid: true, list, list2);
			}
			catch (Exception ex2) when (ex2 is JsonException || ex2 is ArgumentException)
			{
				list2.Add(ex2.Message);
				return new ExtraResourceImportResult(valid: false, Array.Empty<ExtraResourceDefinition>(), list2);
			}
		}

		private static string ReadText(JObject obj, string name)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			JToken obj2 = obj[name];
			if (obj2 == null || (int)obj2.Type != 8)
			{
				throw new ArgumentException(name + " must be text");
			}
			return Extensions.Value<string>((IEnumerable<JToken>)obj[name]) ?? "";
		}

		private static IEnumerable<string> Blocks(string text)
		{
			int index = 0;
			int count = 0;
			while (index < text.Length)
			{
				for (; index < text.Length && (char.IsWhiteSpace(text[index]) || text[index] == ';'); index++)
				{
				}
				if (index == text.Length)
				{
					break;
				}
				if (text[index] == '{')
				{
					int num = count + 1;
					count = num;
					if (num <= 4096)
					{
						int num2 = index;
						int num3 = 0;
						bool flag = false;
						bool flag2 = false;
						for (; index < text.Length; index++)
						{
							char c = text[index];
							if (flag)
							{
								if (flag2)
								{
									flag2 = false;
									continue;
								}
								switch (c)
								{
								case '\\':
									flag2 = true;
									break;
								case '"':
									flag = false;
									break;
								}
								continue;
							}
							switch (c)
							{
							case '"':
								flag = true;
								continue;
							case '{':
								num3++;
								continue;
							case '}':
								break;
							default:
								continue;
							}
							if (--num3 != 0)
							{
								continue;
							}
							index++;
							break;
						}
						if (num3 != 0 || flag)
						{
							throw new ArgumentException("Truncated extra-resource object");
						}
						yield return text.Substring(num2, index - num2);
						continue;
					}
				}
				throw new ArgumentException("Invalid extra-resource object boundary");
			}
		}
	}
	public static class GrowthRules
	{
		private static bool Finite(double n)
		{
			if (!double.IsNaN(n))
			{
				return !double.IsInfinity(n);
			}
			return false;
		}

		public static GrowthResult Evaluate(PlantState state, double worldTime, PlantDefinition definition)
		{
			if (definition == null)
			{
				throw new ArgumentNullException("definition");
			}
			if (!Finite(worldTime) || !Finite(state.Started) || !Finite(state.Duration) || state.Duration < 0.0)
			{
				throw new ArgumentException("Invalid growth clock");
			}
			if (state.Picked && definition.RespawnSeconds == 0.0)
			{
				return new GrowthResult(GrowthPhase.Exhausted, 1.0, 0.0);
			}
			double num = (state.Picked ? definition.RespawnSeconds : state.Duration);
			double num2 = Math.Max(0.0, worldTime - state.Started);
			double fraction = ((num == 0.0) ? 1.0 : Math.Min(1.0, num2 / num));
			double num3 = Math.Max(0.0, num - num2);
			return new GrowthResult(state.Blocked ? GrowthPhase.Blocked : ((num3 == 0.0) ? GrowthPhase.Ready : (state.Picked ? GrowthPhase.Regrowing : GrowthPhase.Growing)), fraction, num3);
		}
	}
	public sealed class LiveCatalogValues
	{
		private sealed class Value
		{
			internal object? Original;

			internal object? Applied;

			internal bool Owned;

			internal Func<object?, object?> Snapshot = Identity;
		}

		private ConditionalWeakTable<object, Dictionary<string, Value>> values = new ConditionalWeakTable<object, Dictionary<string, Value>>();

		private readonly List<WeakReference<object>> targets = new List<WeakReference<object>>();

		private static object? Identity(object? value)
		{
			return value;
		}

		public bool Apply(object target, string member, object? current, object? original, object? effective, Func<object?, object?>? snapshot = null)
		{
			snapshot = snapshot ?? new Func<object, object>(Identity);
			object obj = snapshot(current);
			if (!values.TryGetValue(target, out Dictionary<string, Value> value))
			{
				value = new Dictionary<string, Value>(StringComparer.Ordinal);
				values.Add(target, value);
				targets.Add(new WeakReference<object>(target));
			}
			if (!value.TryGetValue(member, out var value2))
			{
				bool flag = object.Equals(obj, snapshot(effective));
				bool owned = object.Equals(obj, snapshot(original)) || flag;
				Dictionary<string, Value> dictionary = value;
				Value obj2 = new Value
				{
					Original = (flag ? original : current),
					Applied = obj,
					Owned = owned,
					Snapshot = snapshot
				};
				value2 = obj2;
				dictionary[member] = obj2;
			}
			if (!value2.Owned)
			{
				return false;
			}
			if (!object.Equals(obj, value2.Applied))
			{
				value2.Owned = false;
				return false;
			}
			value2.Applied = snapshot(effective);
			return true;
		}

		public void Restore(Func<object, bool> alive, Func<object, string, object?> read, Action<object, string, object?> write)
		{
			foreach (WeakReference<object> target2 in targets)
			{
				if (!target2.TryGetTarget(out var target) || !alive(target) || !values.TryGetValue(target, out Dictionary<string, Value> value))
				{
					continue;
				}
				foreach (KeyValuePair<string, Value> item in value)
				{
					Value value2 = item.Value;
					if (value2.Owned && object.Equals(value2.Snapshot(read(target, item.Key)), value2.Applied))
					{
						write(target, item.Key, value2.Original);
					}
				}
			}
			targets.Clear();
			values = new ConditionalWeakTable<object, Dictionary<string, Value>>();
		}
	}
	public static class ReferenceTraits
	{
		private const int Meadows = 1;

		private const int Swamp = 2;

		private const int BlackForest = 8;

		private const int Plains = 16;

		private const int AshLands = 32;

		private const int Mistlands = 512;

		private const int Farmland = 25;

		private static readonly Dictionary<string, int> biomes = new Dictionary<string, int>
		{
			{ "sapling_turnip", 539 },
			{ "sapling_seedturnip", 539 },
			{ "sapling_carrot", 25 },
			{ "sapling_seedcarrot", 25 },
			{ "sapling_onion", 25 },
			{ "sapling_seedonion", 25 },
			{ "sapling_oat", 25 },
			{ "sapling_poteitr", 25 },
			{ "sapling_Kale", 25 },
			{ "sapling_seedkale", 25 },
			{ "sapling_barley", 16 },
			{ "sapling_flax", 16 },
			{ "sapling_magecap", 512 },
			{ "sapling_jotunpuffs", 512 }
		};

		private static readonly HashSet<string> ashlandsSet = new HashSet<string> { "sapling_turnip", "sapling_seedturnip", "sapling_carrot", "sapling_seedcarrot", "sapling_onion", "sapling_seedonion", "sapling_oat", "sapling_poteitr", "sapling_Kale", "sapling_seedkale" };

		private static readonly Dictionary<string, bool> heat = new Dictionary<string, bool> { { "Ashwood_Sapling", true } };

		private static readonly Dictionary<string, bool> cold = new Dictionary<string, bool>();

		public static IReadOnlyCollection<string> AshlandsCrops => ashlandsSet;

		public static int? Biome(string? prefab, bool ashlandsCrops = true)
		{
			if (prefab == null || !biomes.TryGetValue(prefab, out var value))
			{
				return null;
			}
			return (ashlandsCrops && ashlandsSet.Contains(prefab)) ? (value | 0x20) : value;
		}

		public static bool? Heat(string? prefab)
		{
			if (prefab == null || !heat.TryGetValue(prefab, out var value))
			{
				return null;
			}
			return value;
		}

		public static bool? Cold(string? prefab)
		{
			if (prefab == null || !cold.TryGetValue(prefab, out var value))
			{
				return null;
			}
			return value;
		}
	}
	public sealed class ReferenceMod
	{
		public SourceMod Mod { get; }

		public string ConfigFile { get; }

		public string Prefix { get; }

		public ReferenceMod(string guid, string name, string configFile, string prefix)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			Mod = new SourceMod(guid, name);
			ConfigFile = configFile;
			Prefix = prefix;
		}
	}
	public enum ImportStatus
	{
		Refused,
		NothingFound,
		Blocked,
		Applied
	}
	public sealed class ImportOutcome
	{
		public ImportStatus Status { get; }

		public int Written { get; }

		public IReadOnlyList<string> Report { get; }

		internal ImportOutcome(ImportStatus status, int written, List<string> report)
		{
			Status = status;
			Written = written;
			Report = report.AsReadOnly();
		}
	}
	public static class ReferenceImport
	{
		public static IReadOnlyList<ReferenceMod> Sources { get; } = new ReferenceMod[2]
		{
			new ReferenceMod("advize.PlantEasily", "PlantEasily", "advize.PlantEasily.cfg", "PlantEasily"),
			new ReferenceMod("advize.PlantEverything", "PlantEverything", "advize.PlantEverything.cfg", "PlantEverything")
		};

		public static ConfigConverter Converter(string mappingJson)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			ConversionMapping val = ConversionMapping.Parse(mappingJson);
			return new ConfigConverter(new ConversionMapping(val.Schema, string.IsNullOrEmpty(val.SourceMod) ? "PlantEasily+PlantEverything" : val.SourceMod, 2, (IEnumerable<ConversionRule>)val.Rules));
		}

		public static bool Allowed(SessionRole role)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			return (int)role != 3;
		}

		public static IReadOnlyDictionary<string, string>? ParsePairs(string? text)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			string[] array = (text ?? "").Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length != 0)
				{
					int num = text2.IndexOf('=');
					if (num <= 0 || num == text2.Length - 1)
					{
						return null;
					}
					string text3 = text2.Substring(0, num).Trim();
					string text4 = text2.Substring(num + 1).Trim();
					if (text3.Length == 0 || text4.Length == 0 || dictionary.ContainsKey(text3))
					{
						return null;
					}
					dictionary.Add(text3, text4);
				}
			}
			return dictionary;
		}

		public static ImportOutcome Run(SessionRole role, ConfigConverter converter, IEnumerable<KeyValuePair<string, string>> settings, IConversionTarget target, string? choices, string? bindingOverrides)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (converter == null)
			{
				throw new ArgumentNullException("converter");
			}
			if (settings == null)
			{
				throw new ArgumentNullException("settings");
			}
			if (target == null)
			{
				throw new ArgumentNullException("target");
			}
			List<string> list = new List<string>();
			if (!Allowed(role))
			{
				list.Add("refused: connected to a server; the server owns Wildroot's world settings. Import in single player or on the server.");
				return new ImportOutcome(ImportStatus.Refused, 0, list);
			}
			List<KeyValuePair<string, string>> list2 = settings.ToList();
			Dictionary<string, ConversionRule> dictionary = converter.Mapping.Rules.ToDictionary<ConversionRule, string>((ConversionRule r) => r.Source, StringComparer.Ordinal);
			List<KeyValuePair<string, string>> list3 = new List<KeyValuePair<string, string>>();
			List<string> list4 = new List<string>();
			List<string> list5 = new List<string>();
			foreach (KeyValuePair<string, string> item in list2)
			{
				ConversionRule value3;
				if (item.Key != null && dictionary.TryGetValue(item.Key, out var value) && !value.MetadataOnly && WildrootSettings.Superseded.TryGetValue(value.Target, out string value2))
				{
					list4.Add("superseded " + item.Key + " -> " + value.Target + ": " + value2);
				}
				else if (item.Key != null && dictionary.TryGetValue(item.Key, out value3) && !value3.MetadataOnly && WildrootSettings.UnboundByDefault.Contains(value3.Target))
				{
					list5.Add("kept unbound " + item.Key + " -> " + value3.Target + ": opens from the MeadWorks menu; bind it in Wildroot's settings if wanted.");
				}
				else
				{
					list3.Add(item);
				}
			}
			if (list2.Count == 0)
			{
				list.Add("nothing found: no PlantEasily or PlantEverything settings to import.");
				return new ImportOutcome(ImportStatus.NothingFound, 0, list);
			}
			ConversionPreview val = converter.Preview((IEnumerable<KeyValuePair<string, string>>)list3);
			list.Add("Wildroot import: " + val.Values.Count + " values, " + val.Conflicts.Count + " conflicts, " + val.BindingConflicts.Count + " shortcut collisions, " + val.Errors.Count + " errors, " + val.Unknown.Count + " unknown, " + val.Metadata.Count + " metadata, " + list4.Count + " superseded, " + list5.Count + " kept unbound.");
			foreach (ConversionValue value4 in val.Values)
			{
				list.Add("value " + value4.SourceId + " -> " + value4.TargetId + " = " + Convert.ToString(value4.Value, CultureInfo.InvariantCulture));
			}
			foreach (ConversionConflict conflict in val.Conflicts)
			{
				list.Add("conflict " + conflict.TargetId + ": " + string.Join(" | ", conflict.Choices.Select((ConversionValue c) => c.SourceId + "=" + Convert.ToString(c.Value, CultureInfo.InvariantCulture))) + " (choose with Import.ConflictChoices: " + conflict.TargetId + "=<source>)");
			}
			foreach (ConversionConflict bindingConflict in val.BindingConflicts)
			{
				list.Add("collision " + bindingConflict.TargetId + ": " + string.Join(", ", bindingConflict.Choices.Select((ConversionValue c) => c.TargetId)) + " (set another key with Import.BindingOverrides: <target>=<key>)");
			}
			foreach (string error in val.Errors)
			{
				list.Add("error " + error);
			}
			foreach (string item2 in val.Unknown)
			{
				list.Add("unknown " + item2 + ": skipped (newer reference version?)");
			}
			foreach (string metadatum in val.Metadata)
			{
				list.Add("metadata " + metadatum);
			}
			list.AddRange(list4);
			list.AddRange(list5);
			IReadOnlyDictionary<string, string> readOnlyDictionary = ParsePairs(choices);
			IReadOnlyDictionary<string, string> readOnlyDictionary2 = ParsePairs(bindingOverrides);
			if (readOnlyDictionary == null || readOnlyDictionary2 == null)
			{
				list.Add("blocked: Import.ConflictChoices and Import.BindingOverrides take Target=Value pairs separated by semicolons.");
				return new ImportOutcome(ImportStatus.Blocked, 0, list);
			}
			if (val.Values.Count == 0 && val.Errors.Count == 0)
			{
				list.Add("nothing to import: no reference value maps to a Wildroot setting.");
				return new ImportOutcome(ImportStatus.NothingFound, 0, list);
			}
			try
			{
				ConversionResult val2 = converter.Apply(val, target, readOnlyDictionary, (readOnlyDictionary2.Count == 0) ? null : readOnlyDictionary2);
				list.Add("applied " + val2.Written + " values; the previous Wildroot config was backed up first.");
				return new ImportOutcome((val2.Written == 0) ? ImportStatus.NothingFound : ImportStatus.Applied, val2.Written, list);
			}
			catch (Exception ex) when (ex is ArgumentException || ex is NotSupportedException || ex is InvalidOperationException || ex is FormatException)
			{
				list.Add("blocked: " + ex.Message + " Nothing was written.");
				return new ImportOutcome(ImportStatus.Blocked, 0, list);
			}
		}
	}
	public static class WildrootImportedHelp
	{
		private static readonly Dictionary<string, string> descriptions = new Dictionary<string, string>(StringComparer.Ordinal)
		{
			["General.ToggleKey"] = "Shortcut to turn Wildroot on or off while gameplay input is available. It is unbound by default because F8 opens the shared MeadWorks menu. The server controls the master setting in multiplayer.",
			["Grid.SnapKey"] = "Shortcut to turn snapping to nearby plants on or off. Grid size is unchanged. Unbound by default: use the Grid: SnappingEnabled setting, or hold the controller modifier and click the right stick.",
			["Harvest.ReplantToggleKey"] = "Shortcut to turn replanting after bulk harvest on or off. Replanting still requires the replacement materials and placement permission. Unbound by default: use the Harvest: Replant setting.",
			["Input.IncreaseXKey"] = "While holding the grid modifier, add one column to the planting grid, up to 10.",
			["Input.IncreaseYKey"] = "While holding the grid modifier, add one row to the planting grid, up to 10.",
			["Input.DecreaseXKey"] = "While holding the grid modifier, remove one column from the planting grid, down to 1.",
			["Input.DecreaseYKey"] = "While holding the grid modifier, remove one row from the planting grid, down to 1.",
			["Input.KeyboardModifierKey"] = "Hold this keyboard shortcut to use the grid resize controls while planting.",
			["Input.GamepadModifierKey"] = "Hold this controller button for gardening controls: resize with the directional pad, toggle snapping with the right stick, or bulk-harvest with Use.",
			["Input.KeyboardHarvestModifierKey"] = "Hold this keyboard shortcut while using a ripe resource to harvest nearby resources within Harvest Radius.",
			["Planting.PreventPartialPlanting"] = "On: refuse the entire grid if any cell cannot be planted. Off: plant the cells allowed by Prevent Invalid Planting. Placement permissions still apply to every cell.",
			["Planting.PreventInvalidPlanting"] = "On: skip extra grid cells that fail growth or placement checks. Off: allow extra cells with biological or target failures; the root cell must still be valid and permission failures are never bypassed.",
			["Planting.UseStamina"] = "On: planting with the cultivator uses the native stamina cost. Off: cultivator placement costs no stamina.",
			["Planting.UseDurability"] = "On: cultivator placement uses its native durability cost. Off: planting does not reduce cultivator durability.",
			["General.Enabled"] = "On: enable Wildroot gardening when session permissions allow it. Off: cancel pending gardening work and release Wildroot changes. In multiplayer, the server owns this switch; installed source-mod step-aside still applies.",
			["Grid.SnappingEnabled"] = "On: align the planting grid with nearby plants or pickable resources. Off: place the grid at the unsnapped ghost position.",
			["Grid.Rows"] = "Number of rows in the planting grid, from 1 to 10. With grid planting off, the layout is 1 by 1; Farming row perks can still affect eligible single-crop planting.",
			["Grid.Columns"] = "Number of columns in the planting grid, from 1 to 10. The concurrent placement limit also caps the number of cells.",
			["Planting.RandomizeRotation"] = "On: choose a random heading in 22.5-degree steps for each placement. Off: keep the placement ghost heading. This is independent of position and tilt scatter.",
			["Planting.EnableScatter"] = "On: apply configured position and tilt scatter to new placements. Off: keep grid positions without scatter. Randomize Rotation is a separate switch.",
			["Planting.PositionScatterRadius"] = "Maximum position offset in meters along each horizontal axis when scatter is enabled. Zero keeps the planned grid positions.",
			["Planting.RotationScatterAngle"] = "Maximum pitch and roll offset in degrees when scatter is enabled. Zero adds no tilt; the heading is controlled separately.",
			["Planting.EnableDebugMessages"] = "Legacy import value retained for compatibility. Wildroot does not read this value to enable planting logs.",
			["Grid.GloballyAlignGridDirections"] = "On: use the world-forward direction, rotated by the ghost heading, for unsnapped rows. Off: start with the player-to-ghost direction before applying that heading.",
			["Grid.MinimizeGridSpacing"] = "On: crop spacing uses 1.1 times the grow radius plus the largest relevant capsule radius. Off: use twice the grow radius. Extra Crop Spacing is added in either mode; sapling spacing is separate.",
			["Grid.GridSnappingStyle"] = "Choose Intelligent snapping to orient grid axes around nearby valid space, or Legacy snapping to keep the selected snap axes.",
			["Grid.ForceAltPlacement"] = "On: always use alternate placement when choosing snap candidates and single-crop row perks. Off: use the native alternate-placement input.",
			["Grid.PreferCardinalSnapping"] = "On: prefer forward, back, left or right snap positions when any valid cardinal candidate exists. Off: keep valid diagonal candidates too.",
			["Grid.ExtraCropSpacing"] = "Additional grid spacing in meters for crops, added to their calculated spacing. Saplings use Extra Sapling Spacing instead.",
			["Grid.ExtraSaplingSpacing"] = "Additional grid spacing in meters for saplings, added to 2.2 times their grow radius.",
			["Harvest.Enabled"] = "On: using a ripe resource with the harvest modifier starts a nearby bulk harvest. Off: disable bulk harvest; separate Farming auto-replant perks are not this switch.",
			["Harvest.HarvestStyle"] = "LikeResources harvests nearby ready resources that yield the same item as the target. AllResources includes other ready resources in range. Ward and loaded-area checks still apply.",
			["Harvest.Radius"] = "Bulk-harvest radius in meters around the resource you use. Only ready resources with placement-area and ward access are selected.",
			["Harvest.Replant"] = "On: after confirmed harvest, try to plant a replacement crop at the original position and pay its materials. Off: bulk harvest does not request this replant; eligible Farming auto-replant perks remain separate.",
			["Performance.MaxConcurrentPlacements"] = "Maximum cells and queued or in-flight placements Wildroot accepts at once. The value is at least 1 in use; larger values permit more work, while grid dimensions remain capped at 10 by 10.",
			["Performance.GhostUpdateBatchSize"] = "Maximum extra preview ghosts updated per frame, with at least 1 in use. Larger values refresh a large preview sooner but do more work each frame.",
			["Performance.BulkPlantingBatchSize"] = "Maximum queued placement jobs processed per update, clamped to 1 through 10000 in use. Larger values complete batches sooner but can increase work per frame.",
			["Spacing.Pickables.PreventOverlappingPlacements"] = "On: reject overlapping pickable-resource preview cells. Off: remove this extra overlap check. Terrain and placement permissions still apply.",
			["Spacing.Pickables.DefaultGridSpacing"] = "Fallback grid spacing in meters for pickable resources without a per-prefab spacing or a native harvest radius.",
			["Spacing.Pickables.BlueberryBush GridSpacing"] = "Grid spacing in meters between placements of prefab BlueberryBush. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.CloudberryBush GridSpacing"] = "Grid spacing in meters between placements of prefab CloudberryBush. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.LingonberryBush GridSpacing"] = "Grid spacing in meters between placements of prefab LingonberryBush. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.Pickable_Branch GridSpacing"] = "Grid spacing in meters between placements of prefab Pickable_Branch. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.Pickable_Dandelion GridSpacing"] = "Grid spacing in meters between placements of prefab Pickable_Dandelion. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.Pickable_Fiddlehead GridSpacing"] = "Grid spacing in meters between placements of prefab Pickable_Fiddlehead. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.Pickable_Flint GridSpacing"] = "Grid spacing in meters between placements of prefab Pickable_Flint. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.Pickable_Mushroom GridSpacing"] = "Grid spacing in meters between placements of prefab Pickable_Mushroom. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.Pickable_Mushroom_blue GridSpacing"] = "Grid spacing in meters between placements of prefab Pickable_Mushroom_blue. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.Pickable_Mushroom_yellow GridSpacing"] = "Grid spacing in meters between placements of prefab Pickable_Mushroom_yellow. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.Pickable_SmokePuff GridSpacing"] = "Grid spacing in meters between placements of prefab Pickable_SmokePuff. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.Pickable_Stone GridSpacing"] = "Grid spacing in meters between placements of prefab Pickable_Stone. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.Pickable_Thistle GridSpacing"] = "Grid spacing in meters between placements of prefab Pickable_Thistle. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.Placeable_Stone GridSpacing"] = "Grid spacing in meters between placements of prefab Placeable_Stone. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["Spacing.Pickables.RaspberryBush GridSpacing"] = "Grid spacing in meters between placements of prefab RaspberryBush. This changes placement spacing; it does not change the item's yield or respawn timer.",
			["UI.ShowCost"] = "On: add the grid count or full batch material cost to native requirement text when more than one cell is planned. Off: keep the native requirement display.",
			["Preview.CostDisplayStyle"] = "TotalCount adds the number of planned cells; FullCost shows each requirement amount multiplied by that cell count. Requires Show Cost and a grid larger than one cell.",
			["Preview.CostDisplayLocation"] = "Place the extra grid-count or material-cost text to the left or right of the native requirement text.",
			["Preview.ShowHUDKeyHints"] = "On: show grid modifier and resize keys beside the grid size in the build hints. Off: retain the grid size without those key boxes.",
			["Preview.ShowHoverKeyHints"] = "On: add one hint line to ripe-resource hover text with the bulk-harvest modifier and Use key, plus the replacement crop while Harvest Replant is enabled. Off: omit that line.",
			["Preview.ShowHoverReplantHint"] = "On: include the configured replacement crop in the harvest hint line while Harvest Replant is enabled. Off: leave the crop out. Preview: ShowHoverKeyHints shows or hides the whole line. Actual planting still checks and charges materials.",
			["Preview.ShowGhostsDuringPlacement"] = "On: keep unplaced preview silhouettes visible while a batch is being placed. Off: hide those silhouettes during placement.",
			["Preview.ShowGridDirections"] = "On: draw row and column guide lines for the planting grid. Off: hide the direction-line objects, including the snap line.",
			["Preview.ShowSnapDirection"] = "On: show the guide line from the snapped grid toward its snap source. Off: hide that line. Show Grid Directions must also be enabled.",
			["Preview.HighlightRootGhost"] = "On: tint the healthy root ghost when the grid contains more than one cell. Off: keep its normal ghost appearance.",
			["Preview.RootGhostHighlightColor"] = "Color used for the healthy root ghost highlight. Requires Highlight Root Ghost and more than one grid cell.",
			["Preview.RowStartColor"] = "Color at the start of the row-direction guide line. Requires Show Grid Directions.",
			["Preview.RowEndColor"] = "Color at the end of the row-direction guide line. Requires Show Grid Directions.",
			["Preview.ColumnStartColor"] = "Color at the start of the column-direction guide line. Requires Show Grid Directions.",
			["Preview.ColumnEndColor"] = "Color at the end of the column-direction guide line. Requires Show Grid Directions.",
			["Preview.SnapStartColor"] = "Color at the start of the snap-direction guide line. Requires Show Grid Directions and Show Snap Direction.",
			["Preview.SnapEndColor"] = "Color at the end of the snap-direction guide line. Requires Show Grid Directions and Show Snap Direction.",
			["Catalog.Vanilla.Berries.RaspberryCost"] = "Amount of the first planting material required for Raspberry. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Berries.BlueberryCost"] = "Amount of the first planting material required for Blueberry. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Berries.CloudberryCost"] = "Amount of the first planting material required for Cloudberry. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Berries.LingonberryCost"] = "Amount of the first planting material required for Lingonberry. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Berries.RaspberryRespawnTime"] = "Minutes for Raspberry to replenish after picking. Zero means it does not replenish.",
			["Catalog.Vanilla.Berries.BlueberryRespawnTime"] = "Minutes for Blueberry to replenish after picking. Zero means it does not replenish.",
			["Catalog.Vanilla.Berries.CloudberryRespawnTime"] = "Minutes for Cloudberry to replenish after picking. Zero means it does not replenish.",
			["Catalog.Vanilla.Berries.LingonberryRespawnTime"] = "Minutes for Lingonberry to replenish after picking. Zero means it does not replenish.",
			["Catalog.Vanilla.Berries.RaspberryReturn"] = "Number of the resource's own item received when harvesting Raspberry.",
			["Catalog.Vanilla.Berries.BlueberryReturn"] = "Number of the resource's own item received when harvesting Blueberry.",
			["Catalog.Vanilla.Berries.CloudberryReturn"] = "Number of the resource's own item received when harvesting Cloudberry.",
			["Catalog.Vanilla.Berries.LingonberryReturn"] = "Number of the resource's own item received when harvesting Lingonberry.",
			["Growth.Crops.EnableCropOverrides"] = "On: apply Wildroot crop cost, yield, scale, growth-time and growing-condition overrides. Off: keep the captured native crop values. Other gardening features remain controlled separately.",
			["Growth.Crops.OverrideModdedCrops"] = "On: apply the crop growth and condition overrides to third-party crops too. Off: leave those crops unchanged. Requires Enable Crop Overrides.",
			["Growth.Crops.CropMinScale"] = "Minimum crop scale multiplier when crop overrides are enabled. Must be positive; the effective maximum is raised to at least this value.",
			["Growth.Crops.CropMaxScale"] = "Maximum crop scale multiplier when crop overrides are enabled. Effective maximum cannot be lower than Crop Min Scale.",
			["Growth.Crops.CropGrowTimeMin"] = "Minimum crop growth time in seconds when crop overrides are enabled. Invalid or negative values keep the captured baseline.",
			["Growth.Crops.CropGrowTimeMax"] = "Maximum crop growth time in seconds when crop overrides are enabled. Effective maximum is raised to at least Crop Grow Time Min.",
			["Growth.Crops.CropGrowRadius"] = "Crop growth-clearance radius in meters when crop overrides are enabled. Grid spacing is calculated from this radius too.",
			["Growth.Crops.CropsRequireCultivation"] = "On: crops require cultivated ground. Off: remove that requirement. Requires Enable Crop Overrides; other placement restrictions still apply.",
			["Growth.Crops.CropsRequireSunlight"] = "On: crops need open sky. Off: allow growth under a roof. Requires Enable Crop Overrides.",
			["Growth.Crops.CropsRequireGrowthSpace"] = "On: crops need clear space within their growth radius. Off: remove that growth-space check. Requires Enable Crop Overrides.",
			["Growth.Crops.EnemiesTargetCrops"] = "On: enemies may target the configured crops. Off: remove their normal target flag. Requires Enable Crop Overrides.",
			["Catalog.Vanilla.Crops.BarleyCost"] = "Amount of the first planting material required for Barley. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.BarleyReturn"] = "Number of the resource's own item received when harvesting Barley. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.CarrotCost"] = "Amount of the first planting material required for Carrot. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.CarrotReturn"] = "Number of the resource's own item received when harvesting Carrot. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.FlaxCost"] = "Amount of the first planting material required for Flax. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.FlaxReturn"] = "Number of the resource's own item received when harvesting Flax. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.OnionCost"] = "Amount of the first planting material required for Onion. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.OnionReturn"] = "Number of the resource's own item received when harvesting Onion. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.SeedCarrotCost"] = "Amount of the first planting material required for Seed Carrot. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.SeedCarrotReturn"] = "Number of the resource's own item received when harvesting Seed Carrot. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.SeedOnionCost"] = "Amount of the first planting material required for Seed Onion. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.SeedOnionReturn"] = "Number of the resource's own item received when harvesting Seed Onion. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.SeedTurnipCost"] = "Amount of the first planting material required for Seed Turnip. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.SeedTurnipReturn"] = "Number of the resource's own item received when harvesting Seed Turnip. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.TurnipCost"] = "Amount of the first planting material required for Turnip. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.TurnipReturn"] = "Number of the resource's own item received when harvesting Turnip. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.MagecapCost"] = "Amount of the first planting material required for Magecap. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.MagecapReturn"] = "Number of the resource's own item received when harvesting Magecap. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.JotunPuffsCost"] = "Amount of the first planting material required for Jotun Puffs. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.JotunPuffsReturn"] = "Number of the resource's own item received when harvesting Jotun Puffs. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.OatCost"] = "Amount of the first planting material required for Oat. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.OatReturn"] = "Number of the resource's own item received when harvesting Oat. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.PoteitrCost"] = "Amount of the first planting material required for Poteitr. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.PoteitrReturn"] = "Number of the resource's own item received when harvesting Poteitr. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.KaleCost"] = "Amount of the first planting material required for Kale. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.KaleReturn"] = "Number of the resource's own item received when harvesting Kale. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.SeedKaleCost"] = "Amount of the first planting material required for Seed Kale. Zero disables this catalog entry instead of making planting free. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Crops.SeedKaleReturn"] = "Number of the resource's own item received when harvesting Seed Kale. Applies only while Enable Crop Overrides is on.",
			["Catalog.Vanilla.Debris.PickableBranchCost"] = "Amount of the first planting material required for Pickable Branch. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Debris.PickableBranchReturn"] = "Number of the resource's own item received when harvesting Pickable Branch.",
			["Catalog.Vanilla.Debris.PickableBranchRespawnTime"] = "Minutes for Pickable Branch to replenish after picking. Zero means it does not replenish.",
			["Catalog.Vanilla.Debris.PickableStoneCost"] = "Amount of the first planting material required for Pickable Stone. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Debris.PickableStoneReturn"] = "Number of the resource's own item received when harvesting Pickable Stone.",
			["Catalog.Vanilla.Debris.PickableStoneRespawnTime"] = "Minutes for Pickable Stone to replenish after picking. Zero means it does not replenish.",
			["Catalog.Vanilla.Debris.PickableFlintCost"] = "Amount of the first planting material required for Pickable Flint. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Debris.PickableFlintReturn"] = "Number of the resource's own item received when harvesting Pickable Flint.",
			["Catalog.Vanilla.Debris.PickableFlintRespawnTime"] = "Minutes for Pickable Flint to replenish after picking. Zero means it does not replenish.",
			["Restrictions.RequireCultivation"] = "On: added pickable resources require cultivated ground. Off: they can be placed on ordinary ground. Crop cultivation has its own override setting.",
			["Restrictions.PlaceAnywhere"] = "On: eligible saplings, bushes and decorative plants can leave the ground and ignore roof and growth-space checks. Off: keep those restrictions. Crops, mushrooms, flowers and fixed-ground debris do not gain this behavior; permissions still apply.",
			["Restrictions.EnforceBiomes"] = "On: added cloned saplings and vines use their configured growth biome mask. Off: permit all biomes for those plants. Native plants use Enforce Biomes Vanilla.",
			["Restrictions.EnforceBiomesVanilla"] = "On: native crops and saplings retain their growth biome restrictions. Off: use all biomes. Other growth and shielding checks remain separate.",
			["Restrictions.PlantsRequireShielding"] = "On: plants retain their normal heat and cold tolerances and may need a shield in harsh biomes. Off: configured plants tolerate those environmental conditions.",
			["Restrictions.CanRemoveFlora"] = "On: added decorative flora can be removed. Off: keep that decorative flora non-removable. Other piece kinds follow their existing rules.",
			["Restrictions.RecoverResources"] = "On: recover placement materials when removing added berry bushes, mushrooms or flowers. Off: removing them does not refund those materials.",
			["Restrictions.ResourcesSpawnEmpty"] = "On: added berries, mushrooms, flowers and debris start picked and wait for their respawn timer. Off: they start ready to pick. A zero respawn time does not replenish them.",
			["Restrictions.EnemiesTargetPieces"] = "On: enemies may target added non-crop pieces. Off: remove their normal target flags. Crop overrides have a separate Enemies Target Crops switch.",
			["Catalog.Vanilla.Flowers.ThistleCost"] = "Amount of the first planting material required for Thistle. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Flowers.DandelionCost"] = "Amount of the first planting material required for Dandelion. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Flowers.FiddleheadCost"] = "Amount of the first planting material required for Fiddlehead. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Flowers.ThistleRespawnTime"] = "Minutes for Thistle to replenish after picking. Zero means it does not replenish.",
			["Catalog.Vanilla.Flowers.DandelionRespawnTime"] = "Minutes for Dandelion to replenish after picking. Zero means it does not replenish.",
			["Catalog.Vanilla.Flowers.FiddleheadRespawnTime"] = "Minutes for Fiddlehead to replenish after picking. Zero means it does not replenish.",
			["Catalog.Vanilla.Flowers.ThistleReturn"] = "Number of the resource's own item received when harvesting Thistle.",
			["Catalog.Vanilla.Flowers.DandelionReturn"] = "Number of the resource's own item received when harvesting Dandelion.",
			["Catalog.Vanilla.Flowers.FiddleheadReturn"] = "Number of the resource's own item received when harvesting Fiddlehead.",
			["Catalog.EnableDebugMessages"] = "Legacy import value retained for compatibility. Wildroot does not read this value to enable catalog logs.",
			["Catalog.ShowPickableSpawners"] = "On: retain a visible picked spawner for configured mushrooms, thistle, dandelion and fiddlehead. Off: hide their picked spawner representation.",
			["Catalog.EnableMiscFlora"] = "On: make decorative flora entries available in the cultivator catalog. Off: disable those entries without changing their identities.",
			["Catalog.EnableDebris"] = "On: include branch, stone and flint resource entries in the cultivator catalog. Off: disable those debris entries.",
			["Catalog.EnableExtraResources"] = "On: load extra-resource definitions from the authority file and synchronize them to clients. Off: omit those extra definitions; ordinary catalog entries are unchanged.",
			["Catalog.SnappableVines"] = "On: add Wildroot snap points to the decorative vines piece. Off: remove only those added snap points; native snap points remain intact.",
			["Catalog.EnableLocalization"] = "Legacy import value retained for compatibility. Wildroot uses its shared translation support and Presentation Language instead.",
			["Catalog.Language"] = "Legacy import value retained for compatibility. Select the active interface language with Presentation Language.",
			["Catalog.DisabledResourceNames"] = "Comma-separated prefab names to disable in the cultivator catalog. Names are trimmed and case-sensitive; an empty list disables no entries. Applies to extra resources too.",
			["Catalog.Vanilla.Mushrooms.MushroomCost"] = "Amount of the first planting material required for Mushroom. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Mushrooms.YellowMushroomCost"] = "Amount of the first planting material required for Yellow Mushroom. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Mushrooms.BlueMushroomCost"] = "Amount of the first planting material required for Blue Mushroom. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Mushrooms.SmokepuffCost"] = "Amount of the first planting material required for Smokepuff. Zero disables this catalog entry instead of making planting free.",
			["Catalog.Vanilla.Mushrooms.MushroomRespawnTime"] = "Minutes for Mushroom to replenish after picking. Zero means it does not replenish.",
			["Catalog.Vanilla.Mushrooms.YellowMushroomRespawnTime"] = "Minutes for Yellow Mushroom to replenish after picking. Zero means it does not replenish.",
			["Catalog.Vanilla.Mushrooms.BlueMushroomRespawnTime"] = "Minutes for Blue Mushroom to replenish after picking. Zero means it does not replenish.",
			["Catalog.Vanilla.Mushrooms.SmokepuffRespawnTime"] = "Minutes for Smokepuff to replenish after picking. Zero means it does not replenish.",
			["Catalog.Vanilla.Mushrooms.MushroomReturn"] = "Number of the resource's own item received when harvesting Mushroom.",
			["Catalog.Vanilla.Mushrooms.YellowMushroomReturn"] = "Number of the resource's own item received when harvesting Yellow Mushroom.",
			["Catalog.Vanilla.Mushrooms.BlueMushroomReturn"] = "Number of the resource's own item received when harvesting Blue Mushroom.",
			["Catalog.Vanilla.Mushrooms.SmokepuffReturn"] = "Number of the resource's own item received when harvesting Smokepuff.",
			["Growth.Saplings.OverrideModdedSaplings"] = "On: apply the Modded Sapling growth time, scale and radius to third-party saplings. Off: keep their native growth settings.",
			["Growth.Saplings.ModdedSaplingMinScale"] = "Minimum grown scale multiplier for third-party saplings. Must be positive; the effective maximum is raised to at least this value. Requires Override Modded Saplings.",
			["Growth.Saplings.ModdedSaplingMaxScale"] = "Maximum grown scale multiplier for third-party saplings. Effective maximum cannot be below its minimum scale. Requires Override Modded Saplings.",
			["Growth.Saplings.ModdedSaplingGrowthTime"] = "Growth time in seconds for third-party saplings. The effective minimum is 10 seconds. Requires Override Modded Saplings.",
			["Growth.Saplings.ModdedSaplingGrowRadius"] = "Growth-clearance radius in meters for third-party saplings. This also contributes to grid spacing. Requires Override Modded Saplings.",
			["Growth.Saplings.BirchMinScale"] = "Minimum grown scale multiplier for Birch saplings. Must be positive; the effective maximum is raised to at least this value.",
			["Growth.Saplings.BirchMaxScale"] = "Maximum grown scale multiplier for Birch saplings. Effective maximum cannot be below its minimum scale.",
			["Growth.Saplings.OakMinScale"] = "Minimum grown scale multiplier for Oak saplings. Must be positive; the effective maximum is raised to at least this value.",
			["Growth.Saplings.OakMaxScale"] = "Maximum grown scale multiplier for Oak saplings. Effective maximum cannot be below its minimum scale.",
			["Growth.Saplings.AncientMinScale"] = "Minimum grown scale multiplier for Ancient saplings. Must be positive; the effective maximum is raised to at least this value.",
			["Growth.Saplings.AncientMaxScale"] = "Maximum grown scale multiplier for Ancient saplings. Effective maximum cannot be below its minimum scale.",
			["Growth.Saplings.BirchGrowthTime"] = "Growth time in seconds for Birch saplings. The effective minimum is 10 seconds.",
			["Growth.Saplings.OakGrowthTime"] = "Growth time in seconds for Oak saplings. The effective minimum is 10 seconds.",
			["Growth.Saplings.AncientGrowthTime"] = "Growth time in seconds for Ancient saplings. The effective minimum is 10 seconds.",
			["Growth.Saplings.BirchGrowRadius"] = "Growth-clearance radius in meters for Birch saplings. This also contributes to grid spacing.",
			["Growth.Saplings.OakGrowRadius"] = "Growth-clearance radius in meters for Oak saplings. This also contributes to grid spacing.",
			["Growth.Saplings.AncientGrowRadius"] = "Growth-clearance radius in meters for Ancient saplings. This also contributes to grid spacing.",
			["Growth.Saplings.BeechGrowthTime"] = "Growth time in seconds for Beech saplings. The effective minimum is 10 seconds.",
			["Growth.Saplings.PineGrowthTime"] = "Growth time in seconds for Pine saplings. The effective minimum is 10 seconds.",
			["Growth.Saplings.FirGrowthTime"] = "Growth time in seconds for Fir saplings. The effective minimum is 10 seconds.",
			["Growth.Saplings.BeechMinScale"] = "Minimum grown scale multiplier for Beech saplings. Must be positive; the effective maximum is raised to at least this value.",
			["Growth.Saplings.BeechMaxScale"] = "Maximum grown scale multiplier for Beech saplings. Effective maximum cannot be below its minimum scale.",
			["Growth.Saplings.PineMinScale"] = "Minimum grown scale multiplier for Pine saplings. Must be positive; the effective maximum is raised to at least this value.",
			["Growth.Saplings.PineMaxScale"] = "Maximum grown scale multiplier for Pine saplings. Effective maximum cannot be below its minimum scale.",
			["Growth.Saplings.FirMinScale"] = "Minimum grown scale multiplier for Fir saplings. Must be positive; the effective maximum is raised to at least this value.",
			["Growth.Saplings.FirMaxScale"] = "Maximum grown scale multiplier for Fir saplings. Effective maximum cannot be below its minimum scale.",
			["Growth.Saplings.BeechGrowRadius"] = "Growth-clearance radius in meters for Beech saplings. This also contributes to grid spacing.",
			["Growth.Saplings.PineGrowRadius"] = "Growth-clearance radius in meters for Pine saplings. This also contributes to grid spacing.",
			["Growth.Saplings.FirGrowRadius"] = "Growth-clearance radius in meters for Fir saplings. This also contributes to grid spacing.",
			["Growth.Saplings.YggaMinScale"] = "Minimum grown scale multiplier for Ygga saplings. Must be positive; the effective maximum is raised to at least this value.",
			["Growth.Saplings.YggaMaxScale"] = "Maximum grown scale multiplier for Ygga saplings. Effective maximum cannot be below its minimum scale.",
			["Growth.Saplings.YggaGrowthTime"] = "Growth time in seconds for Ygga saplings. The effective minimum is 10 seconds.",
			["Growth.Saplings.YggaGrowRadius"] = "Growth-clearance radius in meters for Ygga saplings. This also contributes to grid spacing.",
			["Growth.Saplings.AutumnBirchMinScale"] = "Minimum grown scale multiplier for Autumn Birch saplings. Must be positive; the effective maximum is raised to at least this value.",
			["Growth.Saplings.AutumnBirchMaxScale"] = "Maximum grown scale multiplier for Autumn Birch saplings. Effective maximum cannot be below its minimum scale.",
			["Growth.Saplings.AutumnBirchGrowthTime"] = "Growth time in seconds for Autumn Birch saplings. The effective minimum is 10 seconds.",
			["Growth.Saplings.AutumnBirchGrowRadius"] = "Growth-clearance radius in meters for Autumn Birch saplings. This also contributes to grid spacing.",
			["Growth.Saplings.AshwoodMinScale"] = "Minimum grown scale multiplier for Ashwood saplings. Must be positive; the effective maximum is raised to at least this value.",
			["Growth.Saplings.AshwoodMaxScale"] = "Maximum grown scale multiplier for Ashwood saplings. Effective maximum cannot be below its minimum scale.",
			["Growth.Saplings.AshwoodGrowthTime"] = "Growth time in seconds for Ashwood saplings. The effective minimum is 10 seconds.",
			["Growth.Saplings.AshwoodGrowRadius"] = "Growth-clearance radius in meters for Ashwood saplings. This also contributes to grid spacing.",
			["Growth.Saplings.TimberwoodMinScale"] = "Minimum grown scale multiplier for Timberwood saplings. Must be positive; the effective maximum is raised to at least this value.",
			["Growth.Saplings.TimberwoodMaxScale"] = "Maximum grown scale multiplier for Timberwood saplings. Effective maximum cannot be below its minimum scale.",
			["Growth.Saplings.TimberwoodGrowthTime"] = "Growth time in seconds for Timberwood saplings. The effective minimum is 10 seconds.",
			["Growth.Saplings.TimberwoodGrowRadius"] = "Growth-clearance radius in meters for Timberwood saplings. This also contributes to grid spacing.",
			["Drops.EnableSeedOverrides"] = "On: replace tree drop-roll counts and chances and the stack range for seeds, cones, acorns and sap. Off: restore the captured native drop tables.",
			["Drops.seedDropMin"] = "Minimum stack amount for seeds, cones, acorns and sap in tree destruction drops. Requires Enable Seed Overrides; negative values become 0.",
			["Drops.seedDropMax"] = "Maximum stack amount for those planting materials in tree destruction drops. Requires Enable Seed Overrides; effective maximum is at least Seed Drop Min.",
			["Drops.treeDropMin"] = "Minimum number of tree drop rolls when Enable Seed Overrides is on. Negative values become 0; this is not the number of logs or wood in each stack.",
			["Drops.treeDropMax"] = "Maximum number of tree drop rolls when Enable Seed Overrides is on. Effective maximum is at least Tree Drop Min.",
			["Drops.dropChance"] = "Chance of a successful tree drop roll, from 0 to 1, when Enable Seed Overrides is on. 0.5 means 50%; values outside the range are clamped.",
			["Drops.oneOfEach"] = "On: the overridden tree drop table selects each item type at most once. Off: repeated selections are allowed. Requires Enable Seed Overrides.",
			["Hover.EnablePickableTimers"] = "On: append remaining respawn time to hover text for picked, replenishing resources. Off: omit that timer. Resources with no respawn time have no countdown.",
			["Hover.EnablePlantTimers"] = "On: append remaining growth time to a plant's hover text. Off: omit that timer.",
			["Hover.GrowthAsPercentage"] = "On: show completed growth or respawn progress as a whole percentage. Off: show remaining time in hours, minutes and seconds. Requires the matching hover timer.",
			["Growth.Vines.EnableVineOverrides"] = "On: apply vine attachment, growth time, growth radius and berry yield/respawn overrides. Off: keep the captured vine values.",
			["Growth.Vines.VineAttachDistance"] = "Maximum native vine attachment distance in meters when vine overrides are enabled.",
			["Growth.Vines.VineGrowRadius"] = "Growth-clearance radius in meters for vines when vine overrides are enabled.",
			["Growth.Vines.VineGrowthTime"] = "Time in seconds for a planted vine to grow when vine overrides are enabled.",
			["Growth.Vines.VineBerryRespawnTime"] = "Minutes before berries on a grown vine replenish when vine overrides are enabled. Zero gives no replenishment.",
			["Growth.Vines.VineBerryReturn"] = "Number of berry items harvested from a grown vine when vine overrides are enabled."
		};

		public static IEnumerable<string> Keys => descriptions.Keys;

		public static string? Description(string id)
		{
			if (!descriptions.TryGetValue(id, out string value))
			{
				return null;
			}
			return value;
		}

		public static bool IsInternal(string id)
		{
			switch (id)
			{
			case "Planting.EnableDebugMessages":
			case "Catalog.EnableDebugMessages":
			case "Catalog.EnableLocalization":
			case "Catalog.Language":
			case "Farm.ToggleKey":
				return true;
			default:
				return false;
			}
		}
	}
	public enum WildrootPreset
	{
		Vanilla,
		MeadWorksDefault,
		Relaxed,
		Custom
	}
	public sealed class WildrootPresetGroup
	{
		private readonly Dictionary<WildrootPreset, IReadOnlyDictionary<string, object>> choices;

		public string Selector { get; }

		public string[] Members { get; }

		public WildrootPresetGroup(string selector, string[] members, object[] vanilla, object[] defaults, object[] relaxed)
		{
			Selector = selector;
			Members = members;
			choices = new Dictionary<WildrootPreset, IReadOnlyDictionary<string, object>>
			{
				[WildrootPreset.Vanilla] = Values(vanilla),
				[WildrootPreset.MeadWorksDefault] = Values(defaults),
				[WildrootPreset.Relaxed] = Val

plugins/Wildroot/Wildroot.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExtraPotions.Meadworks.Contracts;
using ExtraPotions.Meadworks.Crucible;
using ExtraPotions.Meadworks.Crucible.UI;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ExtraPotions")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.21.0")]
[assembly: AssemblyInformationalVersion("0.1.21+fatal: not a git repository (or any of the parent directories): .git")]
[assembly: AssemblyProduct("Wildroot")]
[assembly: AssemblyTitle("Wildroot")]
[assembly: AssemblyVersion("0.1.21.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 ExtraPotions.Wildroot
{
	internal static class NativeFields
	{
		internal static readonly FieldInfo PlacementGhost = AccessTools.Field(typeof(Player), "m_placementGhost");

		internal static readonly FieldInfo PlacementStatus = AccessTools.Field(typeof(Player), "m_placementStatus");

		internal static readonly FieldInfo NoPlacementCost = AccessTools.Field(typeof(Player), "m_noPlacementCost");

		internal static readonly FieldInfo PlantStatus = AccessTools.Field(typeof(Plant), "m_status");

		internal static readonly FieldInfo PlantHealthy = AccessTools.Field(typeof(Plant), "m_healthy");

		internal static readonly FieldInfo PlantUnhealthy = AccessTools.Field(typeof(Plant), "m_unhealthy");

		internal static readonly MethodInfo GetRightItem = AccessTools.Method(typeof(Humanoid), "GetRightItem", (Type[])null, (Type[])null);

		internal static readonly MethodInfo CanBePicked = AccessTools.Method(typeof(Pickable), "CanBePicked", (Type[])null, (Type[])null);

		internal static readonly MethodInfo GetPicked = AccessTools.Method(typeof(Pickable), "GetPicked", (Type[])null, (Type[])null);

		internal static readonly MethodInfo GetHoneyLevel = AccessTools.Method(typeof(Beehive), "GetHoneyLevel", (Type[])null, (Type[])null);

		internal static readonly MethodInfo TimeSincePlanted = AccessTools.Method(typeof(Plant), "TimeSincePlanted", (Type[])null, (Type[])null);

		internal static readonly MethodInfo GetGrowTime = AccessTools.Method(typeof(Plant), "GetGrowTime", (Type[])null, (Type[])null);

		internal static readonly FieldInfo HudRoot = AccessTools.Field(typeof(Hud), "m_rootObject");
	}
}
namespace ExtraPotions.Wildroot.Runtime
{
	internal static class PlaceAnywhere
	{
		[HarmonyPatch(typeof(Plant), "Grow")]
		private static class GrowPatch
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_0048: Unknown result type (might be due to invalid IL or missing references)
				//IL_004e: Expected O, but got Unknown
				//IL_0071: Unknown result type (might be due to invalid IL or missing references)
				//IL_0077: Expected O, but got Unknown
				//IL_007f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0085: Expected O, but got Unknown
				//IL_0091: Unknown result type (might be due to invalid IL or missing references)
				//IL_0097: Expected O, but got Unknown
				return ((CodeMatcher)Activator.CreateInstance(typeof(CodeMatcher), instructions, null)).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
				{
					new CodeMatch((OpCode?)OpCodes.Callvirt, (object)AccessTools.Method(typeof(TreeBase), "Grow", (Type[])null, (Type[])null), (string)null)
				}).ThrowIfInvalid("Could not patch Plant.Grow (Wildroot PlaceAnywhere)").Advance(1)
					.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[3]
					{
						new CodeInstruction(OpCodes.Ldarg_0, (object)null),
						new CodeInstruction(OpCodes.Ldloc_1, (object)null),
						new CodeInstruction(OpCodes.Call, (object)PropagateMethod)
					})
					.InstructionEnumeration();
			}
		}

		[HarmonyPatch(typeof(Piece), "Awake")]
		private static class PieceReloadPatch
		{
			private static void Postfix(Piece __instance)
			{
				Restore((Component)(object)__instance);
			}
		}

		[HarmonyPatch(typeof(TreeBase), "Awake")]
		private static class TreeReloadPatch
		{
			private static void Postfix(TreeBase __instance)
			{
				Restore((Component)(object)__instance);
			}
		}

		internal const string Marker = "wildroot.placeAnywhere";

		private static readonly MethodInfo PropagateMethod = AccessTools.Method(typeof(PlaceAnywhere), "Propagate", (Type[])null, (Type[])null);

		private static bool Active
		{
			get
			{
				if (Object.op_Implicit((Object)(object)Plugin.Instance) && Plugin.Instance.GameplayAvailable)
				{
					return Plugin.Instance.Settings?.Active ?? false;
				}
				return false;
			}
		}

		internal static void MarkPlaced(GameObject created)
		{
			if (Active && Object.op_Implicit((Object)(object)created))
			{
				ZNetView component = created.GetComponent<ZNetView>();
				StaticPhysics component2 = created.GetComponent<StaticPhysics>();
				if (Object.op_Implicit((Object)(object)component2) && Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner())
				{
					component2.m_fall = false;
					component.GetZDO().Set("wildroot.placeAnywhere", true);
				}
			}
		}

		private static void Propagate(Plant plant, GameObject grownTree)
		{
			if (!Active || !Object.op_Implicit((Object)(object)plant) || !Object.op_Implicit((Object)(object)grownTree))
			{
				return;
			}
			ZNetView component = ((Component)plant).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.GetZDO().GetBool("wildroot.placeAnywhere", false))
			{
				TreeBase component2 = grownTree.GetComponent<TreeBase>();
				StaticPhysics val = (Object.op_Implicit((Object)(object)component2) ? ((Component)component2).GetComponent<StaticPhysics>() : null);
				ZNetView val2 = (Object.op_Implicit((Object)(object)component2) ? ((Component)component2).GetComponent<ZNetView>() : null);
				if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val2) && val2.IsValid() && val2.IsOwner())
				{
					val.m_fall = false;
					val2.GetZDO().Set("wildroot.placeAnywhere", true);
				}
			}
		}

		private static void Restore(Component instance)
		{
			Plugin? instance2 = Plugin.Instance;
			if (instance2 == null || !instance2.Ready)
			{
				return;
			}
			WildrootConfig? settings = Plugin.Instance.Settings;
			if (settings != null && !settings.SteppedAside && Plugin.Instance.Settings.Enabled.Value && Object.op_Implicit((Object)(object)instance))
			{
				ZNetView component = instance.GetComponent<ZNetView>();
				StaticPhysics component2 = instance.GetComponent<StaticPhysics>();
				if (Object.op_Implicit((Object)(object)component2) && Object.op_Implicit((Object)(object)component) && component.IsValid() && component.GetZDO().GetBool("wildroot.placeAnywhere", false))
				{
					component2.m_fall = false;
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class PlantEasilyInputPatches
	{
		private static bool OverrideGamepadInput
		{
			get
			{
				Plugin? instance = Plugin.Instance;
				if (instance != null && instance.GameplayAvailable)
				{
					return Plugin.Instance.Controls?.OverrideGamepadInput ?? false;
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(HotkeyBar), "Update")]
		[HarmonyPatch(typeof(Player), "StartGuardianPower")]
		private static bool Prefix()
		{
			return !OverrideGamepadInput;
		}
	}
	internal static class PickedSpawners
	{
		[HarmonyPatch(typeof(Pickable), "SetPicked")]
		private static class SetPickedPatch
		{
			private static void Postfix(Pickable __instance)
			{
				try
				{
					Sync(__instance);
				}
				catch (Exception)
				{
				}
			}
		}

		[HarmonyPatch(typeof(Pickable), "Awake")]
		private static class AwakePatch
		{
			private static void Postfix(Pickable __instance)
			{
				try
				{
					Sync(__instance);
				}
				catch (Exception)
				{
				}
			}
		}

		internal const string ChildName = "wildroot_picked";

		private const float CutHeight = 0.4f;

		private static readonly Func<Pickable, bool> Picked = AccessTools.MethodDelegate<Func<Pickable, bool>>(NativeFields.GetPicked, (object)null, true);

		internal static void AddTo(GameObject prefab)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: 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_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)prefab) || Object.op_Implicit((Object)(object)prefab.transform.Find("wildroot_picked")))
			{
				return;
			}
			Pickable component = prefab.GetComponent<Pickable>();
			GameObject val = (Object.op_Implicit((Object)(object)component) ? component.m_hideWhenPicked : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			GameObject val2 = new GameObject("wildroot_picked");
			val2.SetActive(false);
			val2.layer = val.layer;
			val2.transform.SetParent(prefab.transform, false);
			val2.transform.localPosition = prefab.transform.InverseTransformPoint(val.transform.position);
			val2.transform.localRotation = Quaternion.Inverse(prefab.transform.rotation) * val.transform.rotation;
			val2.transform.localScale = Vector3.Scale(val.transform.localScale, new Vector3(1f, 0.4f, 1f));
			CopyColliders(val, val2);
			MeshFilter[] componentsInChildren = val.GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val3 in componentsInChildren)
			{
				MeshRenderer component2 = ((Component)val3).GetComponent<MeshRenderer>();
				if (Object.op_Implicit((Object)(object)component2) && Object.op_Implicit((Object)(object)val3.sharedMesh))
				{
					GameObject val4 = (GameObject)(((Object)(object)((Component)val3).gameObject == (Object)(object)val) ? ((object)val2) : ((object)new GameObject(((Object)val3).name)));
					if ((Object)(object)val4 != (Object)(object)val2)
					{
						val4.layer = ((Component)val3).gameObject.layer;
						val4.transform.SetParent(val2.transform, false);
						val4.transform.localPosition = val.transform.InverseTransformPoint(((Component)val3).transform.position);
						val4.transform.localRotation = Quaternion.Inverse(val.transform.rotation) * ((Component)val3).transform.rotation;
						Vector3 lossyScale = val.transform.lossyScale;
						Vector3 lossyScale2 = ((Component)val3).transform.lossyScale;
						val4.transform.localScale = new Vector3(lossyScale2.x / lossyScale.x, lossyScale2.y / lossyScale.y, lossyScale2.z / lossyScale.z);
						CopyColliders(((Component)val3).gameObject, val4);
					}
					val4.AddComponent<MeshFilter>().sharedMesh = val3.sharedMesh;
					((Renderer)val4.AddComponent<MeshRenderer>()).sharedMaterials = ((Renderer)component2).sharedMaterials;
				}
			}
		}

		private static void CopyColliders(GameObject source, GameObject target)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			Collider[] components = source.GetComponents<Collider>();
			foreach (Collider val in components)
			{
				if (!Object.op_Implicit((Object)(object)val) || val.isTrigger)
				{
					continue;
				}
				SphereCollider val2 = (SphereCollider)(object)((val is SphereCollider) ? val : null);
				if (val2 == null)
				{
					CapsuleCollider val3 = (CapsuleCollider)(object)((val is CapsuleCollider) ? val : null);
					if (val3 == null)
					{
						BoxCollider val4 = (BoxCollider)(object)((val is BoxCollider) ? val : null);
						if (val4 != null)
						{
							BoxCollider obj = target.AddComponent<BoxCollider>();
							obj.center = val4.center;
							obj.size = val4.size;
						}
					}
					else
					{
						CapsuleCollider obj2 = target.AddComponent<CapsuleCollider>();
						obj2.center = val3.center;
						obj2.radius = val3.radius;
						obj2.height = val3.height;
						obj2.direction = val3.direction;
					}
				}
				else
				{
					SphereCollider obj3 = target.AddComponent<SphereCollider>();
					obj3.center = val2.center;
					obj3.radius = val2.radius;
				}
			}
		}

		internal static void Sync(Pickable pickable)
		{
			if (!Object.op_Implicit((Object)(object)pickable))
			{
				return;
			}
			Transform val = ((Component)pickable).transform.Find("wildroot_picked");
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			Plugin instance = Plugin.Instance;
			ICatalogSettings val2 = ((!Object.op_Implicit((Object)(object)instance)) ? null : instance.Catalog?.View);
			int num;
			if (Object.op_Implicit((Object)(object)instance) && instance.GameplayAvailable)
			{
				WildrootConfig? settings = instance.Settings;
				if (settings != null && settings.Active && val2 != null && CatalogPolicy.ShowsPickedSpawner(PlacementBackend.PrefabName(((Component)pickable).gameObject), val2))
				{
					num = (Picked(pickable) ? 1 : 0);
					goto IL_008f;
				}
			}
			num = 0;
			goto IL_008f;
			IL_008f:
			bool flag = (byte)num != 0;
			if (((Component)val).gameObject.activeSelf != flag)
			{
				((Component)val).gameObject.SetActive(flag);
			}
		}

		internal static void SyncAll()
		{
			Pickable[] array = Object.FindObjectsByType<Pickable>((FindObjectsSortMode)0);
			for (int i = 0; i < array.Length; i++)
			{
				Sync(array[i]);
			}
		}
	}
	internal static class PlantIconBounds
	{
		internal static (int x, int y, int size) Square(int width, int height, int left, int top, int right, int bottom)
		{
			int num = Math.Min(Math.Min(width, height), Math.Max(right - left + 1, bottom - top + 1) + 4);
			int item = Math.Max(0, Math.Min(width - num, (left + right + 1 - num) / 2));
			int item2 = Math.Max(0, Math.Min(height - num, (top + bottom + 1 - num) / 2));
			return (x: item, y: item2, size: num);
		}
	}
	internal sealed class PrefabRegistrar : IDisposable, IPrefabCatalog
	{
		[HarmonyPatch(typeof(Plant), "Awake")]
		private static class LivePlant
		{
			private static void Postfix(Plant __instance)
			{
				Plugin.Instance?.Catalog?.RefreshInstance((Component)(object)__instance);
			}
		}

		[HarmonyPatch(typeof(Pickable), "Awake")]
		private static class LivePickable
		{
			private static void Postfix(Pickable __instance)
			{
				Plugin.Instance?.Catalog?.RefreshInstance((Component)(object)__instance);
			}
		}

		private sealed class PrefabOriginal
		{
			public bool TolerateHeat;

			public bool TolerateCold;

			public bool NeedCultivated;

			public bool GroundOnly;

			public bool GroundPiece;

			public bool PrimaryTarget;

			public bool PieceCultivated;

			public float AttachDistance;

			public Biome Biome;

			public readonly Dictionary<string, (int amount, float respawn)> Grown = new Dictionary<string, (int, float)>(StringComparer.Ordinal);
		}

		private sealed class ConfigView : ICatalogSettings
		{
			private readonly WildrootConfig settings;

			public ConfigView(WildrootConfig settings)
			{
				this.settings = settings;
			}

			public bool TryGet<T>(string id, out T value)
			{
				ConfigEntryBase? obj = settings.Find(id);
				if (((obj != null) ? obj.BoxedValue : null) is T val)
				{
					value = val;
					return true;
				}
				value = default(T);
				return false;
			}
		}

		private readonly WildrootConfig settings;

		private readonly string assets;

		private readonly ManualLogSource log;

		private readonly ICatalogSettings view;

		private bool registered;

		private bool dirty;

		private CatalogSnapshot? baseline;

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

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

		private readonly HashSet<string> cloned = new HashSet<string>(StringComparer.Ordinal);

		private static readonly Biome AllBiomes = Enum.GetValues(typeof(Biome)).Cast<Biome>().Aggregate((Biome)0, (Biome all, Biome b) => (Biome)(all | b));

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

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

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

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

		private IReadOnlyList<ExtraResourceDefinition> extras = Array.Empty<ExtraResourceDefinition>();

		private FileSystemWatcher? watcher;

		private readonly MutationLedger ledger = new MutationLedger();

		private readonly LiveCatalogValues liveValues = new LiveCatalogValues();

		private readonly Dictionary<Component, HashSet<string>> liveFields = new Dictionary<Component, HashSet<string>>();

		private readonly HashSet<string> nativeCultivator = new HashSet<string>(StringComparer.Ordinal);

		private bool lastActive;

		private ExtraResourceOrigin lastOrigin;

		private bool compareNeeded;

		private readonly CatalogMismatchGate mismatch = new CatalogMismatchGate();

		private const int IconPixels = 256;

		private const string SnapName = "wildroot_snappoint";

		private const string SnapMember = "@snappoints";

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

		public string Fingerprint { get; private set; } = "";

		public CatalogMatch ServerMatch { get; private set; }

		public int RecordedValues => ledger.Count;

		public IReadOnlyDictionary<string, PlantDefinition> Definitions => definitions;

		public bool Registered => registered;

		public int Revision { get; private set; }

		public IReadOnlyList<string> LastWarnings { get; private set; } = Array.Empty<string>();

		internal static string ExtraResourcePath => Path.Combine(Paths.ConfigPath, "Wildroot", "extra-resources.cfg");

		internal ICatalogSettings View => view;

		private ExtraResourceOrigin Origin => CatalogDistribution.Origin(WildrootConfig.Role, settings.Get("Catalog.EnableExtraResources", fallback: false));

		public PrefabRegistrar(WildrootConfig settings, string assets, ManualLogSource log)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			this.settings = settings;
			this.assets = assets;
			this.log = log;
			view = (ICatalogSettings)(object)new ConfigView(settings);
			PrefabManager.OnVanillaPrefabsAvailable += Register;
			PieceManager.OnPiecesRegistered += MarkDirty;
			settings.Changed += OnSettingChanged;
		}

		public bool HasPrefab(string name)
		{
			if (!string.IsNullOrEmpty(name))
			{
				return Object.op_Implicit((Object)(object)PrefabManager.Instance.GetPrefab(name));
			}
			return false;
		}

		public bool HasItem(string name)
		{
			if (HasPrefab(name))
			{
				return Object.op_Implicit((Object)(object)PrefabManager.Instance.GetPrefab(name).GetComponent<ItemDrop>());
			}
			return false;
		}

		internal PiecePolicy? Policy(string prefab)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (!policies.TryGetValue(prefab, out var value))
			{
				return null;
			}
			return value;
		}

		private void OnSettingChanged(string id)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Invalid comparison between Unknown and I4
			switch (id)
			{
			case "Catalog.ServerFingerprint":
				compareNeeded = true;
				return;
			case "Catalog.ExtraResourcesShared":
				if ((int)Origin == 2)
				{
					dirty = true;
				}
				return;
			case "General.Enabled":
				dirty = true;
				break;
			}
			if (id.StartsWith("Catalog.", StringComparison.Ordinal) || id.StartsWith("Growth.", StringComparison.Ordinal) || id.StartsWith("Drops.", StringComparison.Ordinal) || id.StartsWith("Restrictions.", StringComparison.Ordinal))
			{
				dirty = true;
			}
		}

		private void MarkDirty()
		{
			dirty = true;
		}

		private void Register()
		{
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Expected O, but got Unknown
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Expected O, but got Unknown
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Expected O, but got Unknown
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Expected O, but got Unknown
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Expected O, but got Unknown
			//IL_032c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0331: Unknown result type (might be due to invalid IL or missing references)
			if (registered)
			{
				return;
			}
			if (settings.SteppedAside)
			{
				log.LogInfo((object)"Wildroot catalog not registered: a reference mod is installed.");
				return;
			}
			try
			{
				GameObject prefab = PrefabManager.Instance.GetPrefab("Cultivator");
				PieceTable val = ((prefab == null) ? null : prefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_buildPieces);
				if (Object.op_Implicit((Object)(object)val))
				{
					foreach (GameObject piece in val.m_pieces)
					{
						if (Object.op_Implicit((Object)(object)piece))
						{
							nativeCultivator.Add(((Object)piece).name);
						}
					}
				}
				NativePlacementReceipts.Register();
				JArray val2 = (JArray)JObject.Parse(File.ReadAllText(Path.Combine(assets, "catalog", "vanilla-reference.json")))["entries"];
				foreach (JObject item in val2)
				{
					JObject val3 = item;
					string text = ((JToken)val3).Value<string>((object)"prefab");
					if (!HasPrefab(text))
					{
						cloned.Add(text);
						GameObject val4 = PrefabManager.Instance.CreateClonedPrefab(text, "Beech_Sapling");
						if (!Object.op_Implicit((Object)(object)val4))
						{
							throw new InvalidOperationException("Cannot clone missing sapling: " + text);
						}
						Plant component = val4.GetComponent<Plant>();
						string text2 = ((JToken)val3).Value<string>((object)"grownPrefab");
						Set((Component)(object)component, "m_grownPrefabs", new GameObject[1] { PrefabManager.Instance.GetPrefab(text2) });
						PrefabManager.Instance.AddPrefab(new CustomPrefab(val4, false));
					}
				}
				JObject val5 = new JObject();
				val5.Add("schema", JToken.op_Implicit(1));
				val5.Add("entries", (JToken)(object)val2);
				CatalogLoadResult val6 = CatalogLoader.Load(((object)val5).ToString(), (IPrefabCatalog)(object)this);
				if (!val6.Valid)
				{
					throw new InvalidOperationException(string.Join("; ", val6.Errors));
				}
				baseline = val6.Snapshot;
				foreach (PlantDefinition entry in baseline.Entries)
				{
					Configure(entry, PiecePolicies.For(entry, view), applied: false);
					if (cloned.Contains(entry.Prefab))
					{
						PrefabManager.Instance.RemovePrefab(entry.Prefab);
					}
					if (!PieceManager.Instance.AddPiece(new CustomPiece(PrefabManager.Instance.GetPrefab(entry.Prefab), false, new PieceConfig
					{
						PieceTable = "Cultivator",
						Enabled = true
					})))
					{
						throw new InvalidOperationException("Cultivator piece rejected: " + entry.Prefab);
					}
				}
				foreach (string pickedSpawner in CatalogPolicy.PickedSpawners)
				{
					PickedSpawners.AddTo(PrefabManager.Instance.GetPrefab(pickedSpawner));
				}
				registered = true;
				lastActive = settings.Active;
				lastOrigin = Origin;
				Apply();
				log.LogInfo((object)"Wildroot registered PlantEverything catalog; two-reference development baseline.");
			}
			catch (Exception ex)
			{
				log.LogError((object)("Wildroot catalog registration failed: " + ex));
			}
		}

		internal void Update()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (registered)
			{
				bool active = settings.Active;
				ExtraResourceOrigin origin = Origin;
				if (active != lastActive || origin != lastOrigin)
				{
					lastActive = active;
					lastOrigin = origin;
					dirty = true;
				}
				if (dirty)
				{
					dirty = false;
					Apply();
				}
				if (compareNeeded || (mismatch.Pending && (double)Time.realtimeSinceStartup - mismatch.Since >= 10.0))
				{
					compareNeeded = false;
					CompareWithServer();
				}
			}
		}

		internal void Apply()
		{
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			if (baseline == null)
			{
				return;
			}
			if (!settings.Active)
			{
				RestoreNative();
				return;
			}
			try
			{
				List<string> list = new List<string>();
				CatalogSnapshot val = CatalogPolicy.Apply(baseline, view, (IList<string>)list);
				IReadOnlyList<ExtraResourceDefinition> readOnlyList = LoadExtras(list);
				HashSet<string> catalogPrefabs = new HashSet<string>(val.Entries.Select((PlantDefinition d) => d.Prefab), StringComparer.Ordinal);
				Dictionary<string, PlantDefinition> next = new Dictionary<string, PlantDefinition>(StringComparer.Ordinal);
				foreach (PlantDefinition entry in CatalogPolicy.WithExtras(val, readOnlyList, view, (IList<string>)list).Entries)
				{
					next[entry.Prefab] = entry;
				}
				Dictionary<string, ExtraResourceDefinition> dictionary = readOnlyList.Where((ExtraResourceDefinition e) => !catalogPrefabs.Contains(e.Prefab)).GroupBy<ExtraResourceDefinition, string>((ExtraResourceDefinition e) => e.Prefab, StringComparer.Ordinal).ToDictionary<IGrouping<string, ExtraResourceDefinition>, string, ExtraResourceDefinition>((IGrouping<string, ExtraResourceDefinition> g) => g.Key, (IGrouping<string, ExtraResourceDefinition> g) => g.First(), StringComparer.Ordinal);
				Dictionary<string, PiecePolicy> dictionary2 = next.Values.ToDictionary<PlantDefinition, string, PiecePolicy>((PlantDefinition d) => d.Prefab, (PlantDefinition d) => PiecePolicies.For(d, view), StringComparer.Ordinal);
				foreach (PlantDefinition value2 in next.Values)
				{
					Configure(value2, dictionary2[value2.Prefab], applied: true, dictionary.TryGetValue(value2.Prefab, out var value) ? value : null);
				}
				string[] removed = definitions.Keys.Where((string k) => !next.ContainsKey(k)).ToArray();
				definitions.Clear();
				foreach (KeyValuePair<string, PlantDefinition> item in next)
				{
					definitions[item.Key] = item.Value;
				}
				policies.Clear();
				foreach (KeyValuePair<string, PiecePolicy> item2 in dictionary2)
				{
					policies[item2.Key] = item2.Value;
				}
				extras = readOnlyList;
				ApplyTrees();
				SyncCultivator((IReadOnlyCollection<string>)(object)removed);
				Restored = false;
				RefreshLoadedInstances();
				PickedSpawners.SyncAll();
				Revision++;
				LastWarnings = list;
				foreach (string item3 in list)
				{
					log.LogWarning((object)("Wildroot catalog: " + item3));
				}
				Fingerprint = CatalogDistribution.Fingerprint((IEnumerable<PlantDefinition>)definitions.Values);
				PublishFingerprint();
				CompareWithServer();
			}
			catch (Exception ex)
			{
				log.LogError((object)("Wildroot catalog apply rejected; previous catalog kept: " + ex.Message));
			}
		}

		private IReadOnlyList<ExtraResourceDefinition> LoadExtras(List<string> warnings)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Invalid comparison between Unknown and I4
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			ExtraResourceOrigin origin = Origin;
			if ((int)origin != 1)
			{
				watcher?.Dispose();
				watcher = null;
			}
			if ((int)origin == 0)
			{
				Publish(settings.ExtraResourcesShared, "");
				return Array.Empty<ExtraResourceDefinition>();
			}
			if ((int)origin == 2)
			{
				return ParseExtras(settings.ExtraResourcesShared.Value, warnings);
			}
			string extraResourcePath = ExtraResourcePath;
			Directory.CreateDirectory(Path.GetDirectoryName(extraResourcePath));
			if (!File.Exists(extraResourcePath))
			{
				File.WriteAllText(extraResourcePath, "{\"prefabName\":\"PE_FakePrefab1\",\"resourceName\":\"Wood\",\"resourceCost\":1,\"groundOnly\":true,\"pieceName\":\"Example\",\"pieceDescription\":\"Entries starting with PE_Fake are ignored.\"};\n{\n \"prefabName\": \"PE_FakePrefab2\",\n \"resourceName\": \"Stone\",\n \"resourceCost\": 2,\n \"groundOnly\": false,\n \"pieceName\": \"Example two\",\n \"pieceDescription\": \"Any formatting of valid objects is accepted.\"\n}\n");
			}
			if (watcher == null)
			{
				watcher = new FileSystemWatcher(Path.GetDirectoryName(extraResourcePath), Path.GetFileName(extraResourcePath))
				{
					NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite)
				};
				watcher.Changed += delegate
				{
					dirty = true;
				};
				watcher.Created += delegate
				{
					dirty = true;
				};
				watcher.Renamed += delegate
				{
					dirty = true;
				};
				watcher.EnableRaisingEvents = true;
			}
			string text = File.ReadAllText(extraResourcePath);
			IReadOnlyList<ExtraResourceDefinition> result = ParseExtras(text, warnings);
			if (CatalogDistribution.Publishes(WildrootConfig.Role))
			{
				string text2 = CatalogDistribution.Shareable(text);
				if (text2 == null)
				{
					warnings.Add("Extra resources exceed " + 65536 + " characters and are not sent to clients");
					return result;
				}
				Publish(settings.ExtraResourcesShared, text2);
			}
			return result;
		}

		private IReadOnlyList<ExtraResourceDefinition> ParseExtras(string text, List<string> warnings)
		{
			ExtraResourceImportResult val = ExtraResourceImporter.Parse(text, (IPrefabCatalog)(object)this);
			warnings.AddRange(val.Warnings);
			if (!val.Valid)
			{
				warnings.Add("Extra resources rejected; previous definitions kept");
				return extras;
			}
			return val.Entries;
		}

		private void Publish(ConfigEntry<string> entry, string value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if (CatalogDistribution.Publishes(WildrootConfig.Role) && !string.Equals(entry.Value, value, StringComparison.Ordinal))
			{
				MeadworksConfig.Amend<string>(entry, "com.meadworks.wildroot", (ConfigScope)1, value, (string)null, true);
			}
		}

		private void PublishFingerprint()
		{
			Publish(settings.ServerFingerprint, Fingerprint);
		}

		private void CompareWithServer()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			//IL_001e: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			if ((int)WildrootConfig.Role != 3 || Restored)
			{
				ServerMatch = (CatalogMatch)0;
				mismatch.Observe(ServerMatch, "", "", (double)Time.realtimeSinceStartup);
				return;
			}
			ServerMatch = CatalogDistribution.Compare(settings.ServerFingerprint.Value, Fingerprint);
			if (mismatch.Observe(ServerMatch, settings.ServerFingerprint.Value, Fingerprint, (double)Time.realtimeSinceStartup) != null)
			{
				log.LogWarning((object)("Wildroot catalog differs from the server's (server " + settings.ServerFingerprint.Value + ", here " + Fingerprint + "). Every peer needs the same Wildroot version and the same prefab mods for extra resources."));
			}
		}

		private void Configure(PlantDefinition d, PiecePolicy policy, bool applied, ExtraResourceDefinition? extra = null)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Invalid comparison between Unknown and I4
			//IL_0510: Unknown result type (might be due to invalid IL or missing references)
			//IL_0516: Invalid comparison between Unknown and I4
			//IL_0522: Unknown result type (might be due to invalid IL or missing references)
			//IL_06be: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c4: Invalid comparison between Unknown and I4
			//IL_07e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_07e6: Invalid comparison between Unknown and I4
			//IL_046f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0474: Unknown result type (might be due to invalid IL or missing references)
			//IL_0481: Unknown result type (might be due to invalid IL or missing references)
			//IL_048e: Unknown result type (might be due to invalid IL or missing references)
			//IL_049b: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d1: Expected O, but got Unknown
			//IL_0807: Unknown result type (might be due to invalid IL or missing references)
			//IL_0809: Invalid comparison between Unknown and I4
			//IL_05b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_05cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e6: Expected O, but got Unknown
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Invalid comparison between Unknown and I4
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Invalid comparison between Unknown and I4
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_033d: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Invalid comparison between Unknown and I4
			//IL_0346: Unknown result type (might be due to invalid IL or missing references)
			//IL_0348: Invalid comparison between Unknown and I4
			//IL_0377: Unknown result type (might be due to invalid IL or missing references)
			//IL_0379: Invalid comparison between Unknown and I4
			GameObject prefab = PrefabManager.Instance.GetPrefab(d.Prefab);
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				throw new InvalidOperationException("Missing catalog prefab " + d.Prefab);
			}
			PrefabOriginal prefabOriginal = Original(prefab, d);
			CatalogSection val = CatalogPolicy.Section(d);
			bool flag = CatalogPolicy.CropOverrides(view);
			bool flag2 = CatalogPolicy.VineOverrides(view);
			Plant component = prefab.GetComponent<Plant>();
			Pickable component2 = prefab.GetComponent<Pickable>();
			if (Object.op_Implicit((Object)(object)component) && extra == null)
			{
				Write(component, "m_growTime", (float)d.GrowMin);
				Write(component, "m_growTimeMax", (float)d.GrowMax);
				Write(component, "m_growRadius", (float)d.Radius);
				Write(component, "m_minScale", (float)d.ScaleMin);
				Write(component, "m_maxScale", (float)d.ScaleMax);
				if ((int)val == 6)
				{
					ApplySaplingSurvival(component);
				}
				Write(component, "m_tolerateHeat", (ReferenceTraits.Heat(d.Prefab) ?? prefabOriginal.TolerateHeat) || ((PiecePolicy)(ref policy)).ToleratesEnvironment);
				Write(component, "m_tolerateCold", (ReferenceTraits.Cold(d.Prefab) ?? prefabOriginal.TolerateCold) || ((PiecePolicy)(ref policy)).ToleratesEnvironment);
				if ((int)val == 5)
				{
					Write(component, "m_needCultivatedGround", flag ? ((PiecePolicy)(ref policy)).RequiresCultivation : prefabOriginal.NeedCultivated);
				}
				if ((int)val == 7)
				{
					Write(component, "m_attachDistance", (float)(CatalogPolicy.VineAttachDistance(view) ?? ((double)prefabOriginal.AttachDistance)));
				}
				int? num = CatalogPolicy.ReferenceBiome(d.Prefab, view);
				Write(component, "m_biome", (object)(Biome)((!CatalogPolicy.EnforcesBiome(d, !cloned.Contains(d.Prefab), view)) ? ((int)AllBiomes) : (num.HasValue ? num.Value : ((!cloned.Contains(d.Prefab)) ? ((int)prefabOriginal.Biome) : ((int)VegetationBiome(d.GrownPrefab, prefabOriginal.Biome))))));
				GameObject val2 = component.m_grownPrefabs?.FirstOrDefault((Func<GameObject, bool>)((GameObject g) => Object.op_Implicit((Object)(object)g)));
				if (Object.op_Implicit((Object)(object)val2))
				{
					Pickable[] componentsInChildren = val2.GetComponentsInChildren<Pickable>(true);
					foreach (Pickable val3 in componentsInChildren)
					{
						string key = ((Object)val2).name + "/" + ((Object)val3).name;
						if (!prefabOriginal.Grown.TryGetValue(key, out (int, float) value))
						{
							value = (prefabOriginal.Grown[key] = (val3.m_amount, val3.m_respawnTimeMinutes));
						}
						bool flag3 = ((int)val == 5 && flag) || ((int)val == 7 && flag2);
						int num3;
						if (!flag3)
						{
							(num3, _) = value;
						}
						else
						{
							num3 = d.Yield;
						}
						Write(val3, "m_amount", num3);
						if ((int)val == 7)
						{
							Write(val3, "m_respawnTimeMinutes", flag3 ? ((float)(d.RespawnSeconds / 60.0)) : value.Item2);
						}
					}
				}
			}
			if (Object.op_Implicit((Object)(object)component2) && extra == null)
			{
				Write(component2, "m_amount", d.Yield);
				Write(component2, "m_respawnTimeMinutes", (float)(d.RespawnSeconds / 60.0));
				GameObject own = component2.m_itemPrefab;
				DropTable extraDrops = component2.m_extraDrops;
				if (Object.op_Implicit((Object)(object)own) && extraDrops?.m_drops != null && extraDrops.m_drops.Any((DropData e) => (Object)(object)e.m_item == (Object)(object)own))
				{
					Write(component2, "m_extraDrops", (object?)new DropTable
					{
						m_dropMin = extraDrops.m_dropMin,
						m_dropMax = extraDrops.m_dropMax,
						m_dropChance = extraDrops.m_dropChance,
						m_oneOfEach = extraDrops.m_oneOfEach,
						m_drops = extraDrops.m_drops.Where((DropData e) => (Object)(object)e.m_item != (Object)(object)own).ToList()
					});
				}
			}
			Piece val4 = prefab.GetComponent<Piece>();
			if (!Object.op_Implicit((Object)(object)val4))
			{
				val4 = prefab.AddComponent<Piece>();
				val4.m_canBeRemoved = false;
				((StaticTarget)val4).m_randomTarget = false;
				((StaticTarget)val4).m_primaryTarget = false;
			}
			if (Object.op_Implicit((Object)(object)component) && extra == null && (int)d.Kind == 1)
			{
				Write(val4, "m_onlyInBiome", component.m_biome);
			}
			Requirement[] array = ((IEnumerable<ResourceCost>)d.Costs).Select((Func<ResourceCost, Requirement>)((ResourceCost c) => new Requirement
			{
				m_resItem = PrefabManager.Instance.GetPrefab(c.ItemId).GetComponent<ItemDrop>(),
				m_amount = c.Amount,
				m_recover = ((PiecePolicy)(ref policy)).RecoverResources
			})).ToArray();
			Write(val4, "m_resources", array);
			if (!nativeCultivator.Contains(d.Prefab) && extra == null && (Object)(object)Plugin.Instance != (Object)null && !Application.isBatchMode)
			{
				if (!plantIcons.TryGetValue(d.Prefab, out Sprite value2))
				{
					value2 = RenderManager.Instance.Render(new RenderRequest(prefab)
					{
						Width = 256,
						Height = 256,
						Rotation = RenderManager.IsometricRotation,
						ParticleSimulationTime = -1f
					});
					if (Object.op_Implicit((Object)(object)value2))
					{
						value2 = NormalizeIcon(value2);
						plantIcons[d.Prefab] = value2;
					}
				}
				if (Object.op_Implicit((Object)(object)value2))
				{
					Write(val4, "m_icon", value2);
				}
			}
			else if (!Object.op_Implicit((Object)(object)val4.m_icon) && array.Length != 0)
			{
				Write(val4, "m_icon", array[0].m_resItem.m_itemData.GetIcon());
			}
			if (extra != null)
			{
				Write(val4, "m_name", extra.Name);
				Write(val4, "m_description", extra.Description);
				Write(val4, "m_groundOnly", extra.GroundOnly);
				Write(val4, "m_groundPiece", extra.GroundOnly);
			}
			else
			{
				ItemDrop val5 = (((int)d.Kind == 2 && Object.op_Implicit((Object)(object)component2) && Object.op_Implicit((Object)(object)component2.m_itemPrefab)) ? component2.m_itemPrefab.GetComponent<ItemDrop>() : null);
				if (Object.op_Implicit((Object)(object)val5) && !string.IsNullOrEmpty(val5.m_itemData?.m_shared?.m_name))
				{
					Write(val4, "m_name", val5.m_itemData.m_shared.m_name);
				}
				else if (cloned.Contains(d.Prefab))
				{
					Write(val4, "m_name", d.Prefab.Replace('_', ' '));
				}
				else if (string.IsNullOrEmpty(val4.m_name))
				{
					Write(val4, "m_name", d.Prefab);
				}
				Write(val4, "m_groundOnly", ((PiecePolicy)(ref policy)).GroundOnly && prefabOriginal.GroundOnly);
				Write(val4, "m_groundPiece", ((PiecePolicy)(ref policy)).GroundOnly && prefabOriginal.GroundPiece);
			}
			if ((int)d.Kind == 2)
			{
				Write(val4, "m_cultivatedGroundOnly", ((PiecePolicy)(ref policy)).RequiresCultivation);
			}
			else if ((int)val == 5 && extra == null)
			{
				Write(val4, "m_cultivatedGroundOnly", flag ? ((PiecePolicy)(ref policy)).RequiresCultivation : prefabOriginal.PieceCultivated);
			}
			if (d.Id == "vines" && extra == null)
			{
				ledger.Capture((object)prefab, "@snappoints", (object)null);
				SnapPoints(prefab, CatalogPolicy.VineSnapPoints(d, view));
			}
			if (applied)
			{
				Write(val4, "m_canBeRemoved", ((PiecePolicy)(ref policy)).Removable);
				Write(val4, "m_randomTarget", ((PiecePolicy)(ref policy)).Targetable);
				Write(val4, "m_primaryTarget", prefabOriginal.PrimaryTarget && ((PiecePolicy)(ref policy)).Targetable);
			}
		}

		private static Sprite NormalizeIcon(Sprite icon)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			Texture2D texture = icon.texture;
			Color32[] pixels = texture.GetPixels32();
			Rect rect = icon.rect;
			int num = (int)((Rect)(ref rect)).x;
			int num2 = (int)((Rect)(ref rect)).y;
			int num3 = (int)((Rect)(ref rect)).width;
			int num4 = (int)((Rect)(ref rect)).height;
			int num5 = num3;
			int num6 = num4;
			int num7 = -1;
			int num8 = -1;
			for (int i = 0; i < num4; i++)
			{
				for (int j = 0; j < num3; j++)
				{
					if (pixels[(num2 + i) * ((Texture)texture).width + num + j].a > 8)
					{
						num5 = Math.Min(num5, j);
						num7 = Math.Max(num7, j);
						num6 = Math.Min(num6, i);
						num8 = Math.Max(num8, i);
					}
				}
			}
			if (num7 < num5)
			{
				return icon;
			}
			(int, int, int) tuple = PlantIconBounds.Square(num3, num4, num5, num6, num7, num8);
			Sprite result = Sprite.Create(texture, new Rect((float)(num + tuple.Item1), (float)(num2 + tuple.Item2), (float)tuple.Item3, (float)tuple.Item3), new Vector2(0.5f, 0.5f), icon.pixelsPerUnit);
			Object.Destroy((Object)(object)icon);
			return result;
		}

		private static void SnapPoints(GameObject prefab, IReadOnlyList<Point3> points)
		{
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: 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_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			List<Transform> list = (from Transform t in (IEnumerable)prefab.transform
				where ((Object)t).name == "wildroot_snappoint"
				select t).ToList();
			if (list.Count == points.Count && list.Select((Transform t) => t.localPosition).All((Vector3 p) => points.Any((Point3 q) => Vector3.Distance(p, new Vector3((float)((Point3)(ref q)).X, (float)((Point3)(ref q)).Y, (float)((Point3)(ref q)).Z)) < 0.0001f)))
			{
				return;
			}
			foreach (Transform item in list)
			{
				Object.DestroyImmediate((Object)(object)((Component)item).gameObject);
			}
			foreach (Point3 point in points)
			{
				Point3 current = point;
				GameObject val = new GameObject("wildroot_snappoint")
				{
					tag = "snappoint"
				};
				val.SetActive(false);
				val.transform.SetParent(prefab.transform, false);
				val.transform.localPosition = new Vector3((float)((Point3)(ref current)).X, (float)((Point3)(ref current)).Y, (float)((Point3)(ref current)).Z);
			}
		}

		private PrefabOriginal Original(GameObject prefab, PlantDefinition d)
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Invalid comparison between Unknown and I4
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Invalid comparison between Unknown and I4
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			if (originals.TryGetValue(((Object)prefab).name, out PrefabOriginal value))
			{
				return value;
			}
			Plant component = prefab.GetComponent<Plant>();
			Piece component2 = prefab.GetComponent<Piece>();
			value = new PrefabOriginal
			{
				TolerateHeat = (Object.op_Implicit((Object)(object)component) && component.m_tolerateHeat),
				TolerateCold = (Object.op_Implicit((Object)(object)component) && component.m_tolerateCold),
				NeedCultivated = (Object.op_Implicit((Object)(object)component) && component.m_needCultivatedGround),
				AttachDistance = (Object.op_Implicit((Object)(object)component) ? component.m_attachDistance : 0f),
				GroundOnly = (Object.op_Implicit((Object)(object)component2) ? component2.m_groundOnly : ((int)d.Kind != 4 && d.Id != "vines")),
				GroundPiece = (Object.op_Implicit((Object)(object)component2) ? component2.m_groundPiece : ((int)d.Kind != 4 && d.Id != "vines")),
				PrimaryTarget = (Object.op_Implicit((Object)(object)component2) && ((StaticTarget)component2).m_primaryTarget),
				PieceCultivated = (Object.op_Implicit((Object)(object)component2) && component2.m_cultivatedGroundOnly),
				Biome = (Biome)(Object.op_Implicit((Object)(object)component) ? ((int)component.m_biome) : 0)
			};
			originals[((Object)prefab).name] = value;
			return value;
		}

		private void ApplyTrees()
		{
			//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_00ca: Expected O, but got Unknown
			//IL_00cf: Expected O, but got Unknown
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)ZNetScene.instance))
			{
				return;
			}
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				if (!Object.op_Implicit((Object)(object)prefab))
				{
					continue;
				}
				TreeBase component = prefab.GetComponent<TreeBase>();
				if (!Object.op_Implicit((Object)(object)component) || component.m_dropWhenDestroyed == null)
				{
					continue;
				}
				DropTable dropWhenDestroyed = component.m_dropWhenDestroyed;
				if (!treeOriginals.TryGetValue(((Object)prefab).name, out DropTableValues value))
				{
					Dictionary<string, DropTableValues> dictionary = treeOriginals;
					string name = ((Object)prefab).name;
					DropTableValues val = new DropTableValues(dropWhenDestroyed.m_dropMin, dropWhenDestroyed.m_dropMax, (double)dropWhenDestroyed.m_dropChance, dropWhenDestroyed.m_oneOfEach, (IReadOnlyList<DropEntry>)((IEnumerable<DropData>)dropWhenDestroyed.m_drops).Select((Func<DropData, DropEntry>)((DropData e) => new DropEntry(Object.op_Implicit((Object)(object)e.m_item) ? ((Object)e.m_item).name : "", e.m_stackMin, e.m_stackMax))).ToList());
					value = val;
					dictionary[name] = val;
				}
				DropTableValues val2 = TreeDropPolicy.Apply(value, view);
				Write(dropWhenDestroyed, "m_dropMin", val2.Min);
				Write(dropWhenDestroyed, "m_dropMax", val2.Max);
				Write(dropWhenDestroyed, "m_dropChance", (float)val2.Chance);
				Write(dropWhenDestroyed, "m_oneOfEach", val2.OneOfEach);
				ledger.Capture((object)dropWhenDestroyed, "m_drops", (object)new List<DropData>(dropWhenDestroyed.m_drops));
				for (int num = 0; num < dropWhenDestroyed.m_drops.Count && num < val2.Entries.Count; num++)
				{
					DropData value2 = dropWhenDestroyed.m_drops[num];
					DropEntry val3 = val2.Entries[num];
					value2.m_stackMin = ((DropEntry)(ref val3)).StackMin;
					val3 = val2.Entries[num];
					value2.m_stackMax = ((DropEntry)(ref val3)).StackMax;
					dropWhenDestroyed.m_drops[num] = value2;
				}
			}
		}

		private static Biome VegetationBiome(string grown, Biome fallback)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)ZoneSystem.instance) || string.IsNullOrEmpty(grown))
			{
				return fallback;
			}
			Biome val = (Biome)0;
			foreach (ZoneVegetation item in ZoneSystem.instance.m_vegetation)
			{
				if (item != null && item.m_enable && Object.op_Implicit((Object)(object)item.m_prefab) && ((Object)item.m_prefab).name == grown)
				{
					val |= item.m_biome;
				}
			}
			if ((int)val != 0)
			{
				return val;
			}
			return fallback;
		}

		internal static PieceTable? CultivatorTable()
		{
			if (!Object.op_Implicit((Object)(object)ObjectDB.instance))
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Cultivator");
			if (itemPrefab == null)
			{
				return null;
			}
			return itemPrefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_buildPieces;
		}

		private void SyncCultivator(IReadOnlyCollection<string> removed)
		{
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			PieceTable val = CultivatorTable();
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			Write(val, "m_canRemovePieces", true);
			List<GameObject> pieces = val.m_pieces;
			foreach (string item in removed)
			{
				GameObject prefab = PrefabManager.Instance.GetPrefab(item);
				if (Object.op_Implicit((Object)(object)prefab))
				{
					pieces.Remove(prefab);
				}
			}
			foreach (PlantDefinition value2 in definitions.Values)
			{
				GameObject prefab2 = PrefabManager.Instance.GetPrefab(value2.Prefab);
				if (Object.op_Implicit((Object)(object)prefab2))
				{
					bool flag = pieces.Contains(prefab2);
					if (value2.Enabled && !flag)
					{
						pieces.Add(prefab2);
					}
					else if (!value2.Enabled && flag)
					{
						pieces.Remove(prefab2);
					}
				}
			}
			GrowthOverride? val2 = CatalogPolicy.ModdedCrop(view);
			GrowthOverride? val3 = CatalogPolicy.ModdedSapling(view);
			GameObject[] array = pieces.ToArray();
			foreach (GameObject val4 in array)
			{
				if (!Object.op_Implicit((Object)(object)val4) || definitions.ContainsKey(((Object)val4).name))
				{
					continue;
				}
				Plant component = val4.GetComponent<Plant>();
				if (!Object.op_Implicit((Object)(object)component))
				{
					continue;
				}
				if (!moddedOriginals.TryGetValue(((Object)val4).name, out var value))
				{
					Dictionary<string, GrowthOverride> dictionary = moddedOriginals;
					string name = ((Object)val4).name;
					((GrowthOverride)(ref value))..ctor((double)component.m_growTime, (double)component.m_growTimeMax, (double)component.m_growRadius, (double)component.m_minScale, (double)component.m_maxScale);
					dictionary[name] = value;
				}
				int num;
				GrowthOverride? val5;
				if (component.m_grownPrefabs != null)
				{
					num = (component.m_grownPrefabs.Any((GameObject g) => Object.op_Implicit((Object)(object)g) && Object.op_Implicit((Object)(object)g.GetComponent<TreeBase>())) ? 1 : 0);
					if (num != 0)
					{
						val5 = val3;
						goto IL_01fb;
					}
				}
				else
				{
					num = 0;
				}
				val5 = val2;
				goto IL_01fb;
				IL_01fb:
				GrowthOverride val6 = val5 ?? value;
				Write(component, "m_growTime", (float)((GrowthOverride)(ref val6)).GrowMin);
				Write(component, "m_growTimeMax", (float)((GrowthOverride)(ref val6)).GrowMax);
				Write(component, "m_growRadius", (float)((GrowthOverride)(ref val6)).Radius);
				Write(component, "m_minScale", (float)((GrowthOverride)(ref val6)).ScaleMin);
				Write(component, "m_maxScale", (float)((GrowthOverride)(ref val6)).ScaleMax);
				if (num != 0)
				{
					ApplySaplingSurvival(component);
				}
			}
		}

		private void ApplySaplingSurvival(Plant plant)
		{
			RecordedValue obj = ledger.Original((object)plant, "m_destroyIfCantGrow");
			bool flag = ((((obj != null) ? obj.Original : null) is bool flag2) ? flag2 : plant.m_destroyIfCantGrow);
			Write(plant, "m_destroyIfCantGrow", !settings.Get("Restrictions.PlaceAnywhere", fallback: false) && flag);
		}

		private bool PlaceAnywhereEligible(GameObject created)
		{
			if (!Object.op_Implicit((Object)(object)created) || !definitions.TryGetValue(PlacementBackend.PrefabName(created), out PlantDefinition value))
			{
				return false;
			}
			Piece component = created.GetComponent<Piece>();
			if (value.Enabled && settings.Get("Restrictions.PlaceAnywhere", fallback: false) && policies.TryGetValue(value.Prefab, out var value2) && !((PiecePolicy)(ref value2)).GroundOnly && Object.op_Implicit((Object)(object)component))
			{
				return !component.m_groundOnly;
			}
			return false;
		}

		internal void AfterCreated(GameObject created)
		{
			if (PlaceAnywhereEligible(created))
			{
				PlaceAnywhere.MarkPlaced(created);
			}
		}

		internal void AfterCommitted(GameObject created)
		{
			if (Object.op_Implicit((Object)(object)created) && definitions.TryGetValue(PlacementBackend.PrefabName(created), out PlantDefinition value) && PiecePolicies.SpawnsEmpty(value, view))
			{
				Pickable component = created.GetComponent<Pickable>();
				if (Object.op_Implicit((Object)(object)component))
				{
					AccessTools.Method(typeof(Pickable), "SetPicked", (Type[])null, (Type[])null).Invoke(component, new object[1] { true });
				}
			}
		}

		private void Write(object target, string field, object? value)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			if (target != null)
			{
				Object val = (Object)((target is Object) ? target : null);
				if (val == null || Object.op_Implicit(val))
				{
					FieldInfo fieldInfo = AccessTools.Field(target.GetType(), field) ?? throw new MissingFieldException(field);
					ledger.Capture(target, field, fieldInfo.GetValue(target));
					fieldInfo.SetValue(target, value);
					if (IsLiveField(target, field))
					{
						Component key = (Component)target;
						if (!liveFields.TryGetValue(key, out HashSet<string> value2))
						{
							value2 = (liveFields[key] = new HashSet<string>(StringComparer.Ordinal));
						}
						value2.Add(field);
					}
					return;
				}
			}
			throw new InvalidOperationException("Missing prefab component");
		}

		private static bool IsLiveField(object target, string field)
		{
			if (target is Plant)
			{
				switch (field)
				{
				case "m_growTime":
				case "m_growTimeMax":
				case "m_minScale":
				case "m_maxScale":
				case "m_growRadius":
				case "m_tolerateHeat":
				case "m_tolerateCold":
				case "m_needCultivatedGround":
				case "m_attachDistance":
				case "m_biome":
				case "m_destroyIfCantGrow":
					return true;
				}
			}
			if (target is Pickable)
			{
				if (!(field == "m_amount") && !(field == "m_respawnTimeMinutes"))
				{
					return field == "m_extraDrops";
				}
				return true;
			}
			return false;
		}

		private void RefreshLoadedInstances()
		{
			Plant[] array = Object.FindObjectsByType<Plant>((FindObjectsInactive)1, (FindObjectsSortMode)0);
			foreach (Plant instance in array)
			{
				RefreshInstance((Component)(object)instance);
			}
			Pickable[] array2 = Object.FindObjectsByType<Pickable>((FindObjectsInactive)1, (FindObjectsSortMode)0);
			foreach (Pickable instance2 in array2)
			{
				RefreshInstance((Component)(object)instance2);
			}
		}

		internal void RefreshInstance(Component instance)
		{
			if (Restored || !settings.Active || !Object.op_Implicit((Object)(object)instance))
			{
				return;
			}
			ZNetView componentInParent = instance.GetComponentInParent<ZNetView>();
			Transform val = (Object.op_Implicit((Object)(object)componentInParent) ? ((Component)componentInParent).transform : instance.transform);
			GameObject prefab = PrefabManager.Instance.GetPrefab(PlacementBackend.PrefabName(((Component)val).gameObject));
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return;
			}
			string text = "";
			Transform val2 = instance.transform;
			while ((Object)(object)val2 != (Object)(object)val && Object.op_Implicit((Object)(object)val2))
			{
				text = ((text.Length == 0) ? ((Object)val2).name : (((Object)val2).name + "/" + text));
				val2 = val2.parent;
			}
			Transform val3 = ((text.Length == 0) ? prefab.transform : prefab.transform.Find(text));
			if (!Object.op_Implicit((Object)(object)val3))
			{
				return;
			}
			Component component = ((Component)val3).GetComponent(((object)instance).GetType());
			if (!Object.op_Implicit((Object)(object)component) || (Object)(object)component == (Object)(object)instance || !liveFields.TryGetValue(component, out HashSet<string> value))
			{
				return;
			}
			foreach (string item in value)
			{
				FieldInfo fieldInfo = AccessTools.Field(((object)instance).GetType(), item);
				RecordedValue val4 = ledger.Original((object)component, item);
				if (val4 != null)
				{
					object value2 = fieldInfo.GetValue(instance);
					object value3 = fieldInfo.GetValue(component);
					if (liveValues.Apply((object)instance, item, value2, val4.Original, value3, (Func<object, object>)SnapshotLiveValue))
					{
						fieldInfo.SetValue(instance, value3);
					}
				}
			}
		}

		private static object? SnapshotLiveValue(object? value)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			DropTable val = (DropTable)((value is DropTable) ? value : null);
			if (val == null)
			{
				return value;
			}
			StringBuilder signature = new StringBuilder();
			Fields(val, "m_drops");
			if (val.m_drops == null)
			{
				signature.Append("null");
			}
			else
			{
				foreach (DropData drop in val.m_drops)
				{
					signature.Append('{');
					Fields(drop, "");
					signature.Append('}');
				}
			}
			return signature.ToString();
			void Fields(object item, string excluded)
			{
				foreach (FieldInfo item in item.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public).OrderBy<FieldInfo, string>((FieldInfo f) => f.Name, StringComparer.Ordinal))
				{
					if (!(item.Name == excluded))
					{
						object value2 = item.GetValue(item);
						StringBuilder stringBuilder = signature.Append(item.Name).Append(':');
						Object val2 = (Object)((value2 is Object) ? value2 : null);
						stringBuilder.Append((val2 == null) ? Convert.ToString(value2, CultureInfo.InvariantCulture) : (Object.op_Implicit(val2) ? val2.GetInstanceID().ToString(CultureInfo.InvariantCulture) : "null")).Append(';');
					}
				}
			}
		}

		internal void RestoreNative()
		{
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			PieceTable val = CultivatorTable();
			if (Object.op_Implicit((Object)(object)val))
			{
				RestoreCultivator(val);
			}
			liveValues.Restore((Func<object, bool>)delegate(object target)
			{
				Component val3 = (Component)((target is Component) ? target : null);
				return val3 != null && Object.op_Implicit((Object)(object)val3);
			}, (Func<object, string, object>)((object target, string field) => AccessTools.Field(target.GetType(), field).GetValue(target)), (Action<object, string, object>)delegate(object target, string field, object? value)
			{
				AccessTools.Field(target.GetType(), field).SetValue(target, value);
			});
			if (Restored && ledger.Count == 0)
			{
				return;
			}
			int num = 0;
			foreach (RecordedValue item in ledger.Restore())
			{
				try
				{
					object handle = item.Handle;
					Object val2 = (Object)((handle is Object) ? handle : null);
					if (val2 != null && !Object.op_Implicit(val2))
					{
						continue;
					}
					if (item.Member == "@snappoints")
					{
						foreach (Transform item2 in (from Transform t in (IEnumerable)((GameObject)item.Handle).transform
							where ((Object)t).name == "wildroot_snappoint"
							select t).ToList())
						{
							Object.DestroyImmediate((Object)(object)((Component)item2).gameObject);
						}
					}
					else
					{
						AccessTools.Field(item.Handle.GetType(), item.Member).SetValue(item.Handle, item.Original);
					}
					num++;
				}
				catch (Exception ex)
				{
					log.LogWarning((object)("Wildroot could not restore " + item.Member + ": " + ex.Message));
				}
			}
			Restored = true;
			Fingerprint = "";
			ServerMatch = (CatalogMatch)0;
			PickedSpawners.SyncAll();
			Revision++;
			log.LogInfo((object)("Wildroot restored " + num + " native values; the game's own planting rules apply."));
		}

		private void RestoreCultivator(PieceTable table)
		{
			Dictionary<string, PlantDefinition>.KeyCollection keys = definitions.Keys;
			CatalogSnapshot? obj = baseline;
			IEnumerable<string> enumerable = keys.Concat(((obj != null) ? obj.Entries.Select((PlantDefinition d) => d.Prefab) : null) ?? Enumerable.Empty<string>()).Concat(extras.Select((ExtraResourceDefinition e) => e.Prefab));
			Dictionary<string, GameObject> dictionary = new Dictionary<string, GameObject>(StringComparer.Ordinal);
			foreach (GameObject piece in table.m_pieces)
			{
				if (Object.op_Implicit((Object)(object)piece) && !dictionary.ContainsKey(((Object)piece).name))
				{
					dictionary[((Object)piece).name] = piece;
				}
			}
			IReadOnlyList<string> readOnlyList = CultivatorMembership.Restore(from p in table.m_pieces
				where Object.op_Implicit((Object)(object)p)
				select ((Object)p).name, (IEnumerable<string>)nativeCultivator, enumerable);
			table.m_pieces.Clear();
			foreach (string item in readOnlyList)
			{
				GameObject value;
				GameObject val = (dictionary.TryGetValue(item, out value) ? value : PrefabManager.Instance.GetPrefab(item));
				if (Object.op_Implicit((Object)(object)val))
				{
					table.m_pieces.Add(val);
				}
			}
		}

		private static void Set(Component target, string field, object value)
		{
			if (!Object.op_Implicit((Object)(object)target))
			{
				throw new InvalidOperationException("Missing prefab component");
			}
			FieldInfo fieldInfo = AccessTools.Field(((object)target).GetType(), field);
			if (fieldInfo == null)
			{
				throw new MissingFieldException(field);
			}
			fieldInfo.SetValue(target, value);
		}

		public void Dispose()
		{
			try
			{
				RestoreNative();
			}
			catch (Exception ex)
			{
				log.LogWarning((object)("Wildroot restore on shutdown failed: " + ex.Message));
			}
			PrefabManager.OnVanillaPrefabsAvailable -= Register;
			PieceManager.OnPiecesRegistered -= MarkDirty;
			settings.Changed -= OnSettingChanged;
			watcher?.Dispose();
			watcher = null;
		}
	}
	internal sealed class ReferenceImportTarget : IValidatedConversionTarget, IConversionTarget
	{
		private readonly ConfigFile config;

		private readonly IConversionTarget target;

		private readonly string[] sources;

		private readonly string sourceHash;

		private readonly string targetHash;

		private readonly long generation;

		private readonly SessionRole role;

		private bool backupRequested;

		private bool written;

		internal bool SavedLocally => written;

		public int SchemaVersion => target.SchemaVersion;

		internal ReferenceImportTarget(ConfigFile config, IEnumerable<string> sourcePaths)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			this.config = config ?? throw new ArgumentNullException("config");
			target = MeadworksConversion.Target(config);
			sources = sourcePaths.Select(Path.GetFullPath).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
			generation = Plugin.Session.Generation;
			role = WildrootConfig.Role;
			sourceHash = SourceHash();
			targetHash = TargetHash();
		}

		private static string Read(string path)
		{
			if (!File.Exists(path))
			{
				return "missing";
			}
			using FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
			if (fileStream.Length > 1048576)
			{
				throw new IOException("Import configuration exceeds 1 MiB.");
			}
			using SHA256 sHA = SHA256.Create();
			return BitConverter.ToString(sHA.ComputeHash(fileStream)).Replace("-", "");
		}

		private static string Hash(string text)
		{
			using SHA256 sHA = SHA256.Create();
			return BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(text))).Replace("-", "");
		}

		private string SourceHash()
		{
			return Hash(string.Join("\n", sources.Select((string path) => path + "=" + Read(path))));
		}

		private string TargetHash()
		{
			return Hash(Read(config.ConfigFilePath) + "\n" + string.Join("\n", from k in config.Keys.OrderBy<ConfigDefinition, string>((ConfigDefinition k) => k.Section, StringComparer.Ordinal).ThenBy<ConfigDefinition, string>((ConfigDefinition k) => k.Key, StringComparer.Ordinal)
				select k.Section + "." + k.Key + "=" + TomlTypeConverter.ConvertToString(config[k].BoxedValue, config[k].SettingType)));
		}

		private ConfigEntryBase Entry(string id)
		{
			ConfigDefinition[] array = config.Keys.Where((ConfigDefinition k) => k.Section + "." + k.Key == id).Take(2).ToArray();
			if (array.Length != 1)
			{
				throw new ArgumentException("Unknown or ambiguous Wildroot import target: " + id);
			}
			return config[array[0]];
		}

		private void Admission()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (written)
			{
				throw new InvalidOperationException("This Wildroot import has already been applied.");
			}
			if (!Plugin.IsReady || !ReferenceImport.Allowed(WildrootConfig.Role) || role != WildrootConfig.Role || generation != Plugin.Session.Generation || sourceHash != SourceHash() || targetHash != TargetHash())
			{
				throw new InvalidOperationException("Import source, target or session changed; request the import again.");
			}
		}

		private ConfigEntryBase[] Entries(IReadOnlyDictionary<string, object> values)
		{
			List<ConfigEntryBase> list = new List<ConfigEntryBase>();
			ConfigEntryMetadata val2 = default(ConfigEntryMetadata);
			foreach (KeyValuePair<string, object> value in values)
			{
				ConfigEntryBase val = Entry(value.Key);
				if (!MeadworksConfig.TryGetMetadata(val, ref val2) || val2.ProductId != "com.meadworks.wildroot" || !val2.Decision.Allowed || val2.Decision.RequiresServerAmendment)
				{
					throw new InvalidOperationException("Import on the authority machine while this setting is editable: " + value.Key);
				}
				object obj = (val.SettingType.IsInstanceOfType(value.Value) ? value.Value : TomlTypeConverter.ConvertToValue(Convert.ToString(value.Value, CultureInfo.InvariantCulture), val.SettingType));
				if (val.Description.AcceptableValues != null && !val.Description.AcceptableValues.IsValid(obj))
				{
					throw new ArgumentException("Invalid Wildroot import target value: " + value.Key);
				}
				list.Add(val);
			}
			return list.Distinct().ToArray();
		}

		public void Validate(IReadOnlyDictionary<string, object> values)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			Admission();
			((IValidatedConversionTarget)target).Validate(values);
			Entries(values);
		}

		public void Backup()
		{
			Admission();
			if (!File.Exists(config.ConfigFilePath))
			{
				throw new IOException("Save Wildroot configuration before importing.");
			}
			backupRequested = true;
		}

		public void WriteAtomic(IReadOnlyDictionary<string, object> values)
		{
			Validate(values);
			if (!backupRequested)
			{
				throw new InvalidOperationException("Wildroot import requires a backup.");
			}
			ConfigEntryBase[] array = Entries(values);
			if (array.Length == 0)
			{
				return;
			}
			try
			{
				if (!MeadworksConfig.AmendBatch("com.meadworks.wildroot", (IReadOnlyList<ConfigEntryBase>)array, (Action)delegate
				{
					Admission();
					Entries(values);
					target.Backup();
					target.WriteAtomic(values);
					written = true;
				}, "Wildroot").Applied || !written)
				{
					throw new InvalidOperationException("Current authority or synchronization refused the Wildroot import.");
				}
			}
			catch (Exception innerException) when (written)
			{
				throw new IOException("Wildroot settings were saved locally, but synchronization did not complete.", innerException);
			}
		}
	}
	internal sealed class FarmingFeatures : IDisposable
	{
		private readonly WildrootConfig settings;

		private readonly LocalizationLoader? text;

		private IDisposable? experience;

		private IDisposable? page;

		private readonly List<CustomRecipe> recipes = new List<CustomRecipe>();

		private bool recipesOn;

		internal bool RecipesEnabled => recipesOn;

		internal IReadOnlyList<CustomRecipe> Recipes => recipes;

		internal FarmingFeatures(WildrootConfig settings, LocalizationLoader? text)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Expected O, but got Unknown
			this.settings = settings;
			this.text = text;
			experience = MeadworksSkills.RegisterExperience("com.meadworks.wildroot", (SkillType)106, (Func<Player, float, float>)FarmingExperience.Scale);
			page = MeadworksSkills.RegisterPage("com.meadworks.wildroot", (SkillType)106, (Func<Player, float, IReadOnlyList<SkillPageLine>>)Page);
			foreach (SeedRecipe item in SeedRecipes.All)
			{
				RecipeConfig val = new RecipeConfig();
				val.Name = item.Name;
				val.Item = item.Seed;
				val.Amount = item.Amount;
				val.CraftingStation = item.Station;
				val.MinStationLevel = item.StationLevel;
				val.Enabled = false;
				val.Requirements = (RequirementConfig[])(object)new RequirementConfig[1]
				{
					new RequirementConfig(item.Crop, item.Cost, 0, true)
				};
				CustomRecipe val2 = new CustomRecipe(val);
				if (ItemManager.Instance.AddRecipe(val2))
				{
					recipes.Add(val2);
				}
			}
		}

		internal void Update()
		{
			Plugin instance = Plugin.Instance;
			recipesOn = Object.op_Implicit((Object)(object)instance) && instance.GameplayAvailable && settings.Active && settings.Get("SeedCrafting.Enabled", fallback: true);
			foreach (CustomRecipe recipe2 in recipes)
			{
				Recipe recipe = recipe2.Recipe;
				if (Object.op_Implicit((Object)(object)recipe) && recipe.m_enabled != recipesOn)
				{
					recipe.m_enabled = recipesOn;
				}
			}
		}

		private IReadOnlyList<SkillPageLine> Page(Player player, float level)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Expected O, but got Unknown
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Expected O, but got Unknown
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Expected O, but got Unknown
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Expected O, but got Unknown
			List<SkillPageLine> list = new List<SkillPageLine>();
			FarmingRules rules = FarmingState.Rules;
			if (!rules.Enabled)
			{
				Game(list, player, level);
				list.Add(new SkillPageLine(T("farm_bonus_any"), false, T("farm_bonus_title"), T("farm_bonus_any_tip")));
				list.Add(new SkillPageLine(T("farm_off"), false, "", ""));
				return list;
			}
			list.Add(new SkillPageLine(T("farm_about"), false, "", ""));
			Game(list, player, level);
			list.Add(new SkillPageLine(F("farm_bonus", FarmingPage.Percent(FarmingPerks.Factor(level) * FarmingPerks.BonusChance(rules, 0.25f))), false, T("farm_bonus_title"), T("farm_bonus_tip")));
			if (rules.SeedReturnChanceAt100 > 0f)
			{
				list.Add(new SkillPageLine(F("farm_seed", FarmingPage.Percent(FarmingPerks.SeedReturnChance(rules, level))), false, T("farm_seed_title"), T("farm_seed_tip")));
			}
			if (rules.GrowthSpeedAt100 > 0f)
			{
				list.Add(new SkillPageLine(F("farm_growth", FarmingPage.Percent(FarmingPerks.Share(rules.GrowthSpeedAt100, level))), false, T("farm_growth_title"), (rules.RainGrowthBonus > 0f) ? F("farm_growth_tip_rain", Number(rules.RainGrowthBonus)) : T("farm_growth_tip")));
			}
			if (rules.GrowSpaceReductionAt100 > 0f)
			{
				list.Add(new SkillPageLine(F("farm_spacing", FarmingPage.Percent(Math.Min(0.8f, FarmingPerks.Share(rules.GrowSpaceReductionAt100, level)))), false, T("farm_spacing_title"), T("farm_spacing_tip")));
			}
			list.Add(new SkillPageLine(T("farm_perks"), true, "", ""));
			string text = Key("$KEY_AltPlace", "Shift");
			Perk(list, level, T("farm_row3"), rules.RowOfThreeLevel, F("farm_row_tip", 3, text) + Own(rules.RowPlanting, "RowPlanting"));
			Perk(list, level, T("farm_row5"), rules.RowOfFiveLevel, F("farm_row_tip", 5, text) + Own(rules.RowPlanting, "RowPlanting"));
			Perk(list, level, T("farm_replant"), rules.AutoReplantLevel, T("farm_replant_tip") + Own(rules.AutoReplant, "AutoReplant"));
			Perk(list, level, T("farm_heat"), rules.HeatToleranceLevel, T("farm_heat_tip"));
			Perk(list, level, T("farm_cold"), rules.ColdToleranceLevel, T("farm_cold_tip"));
			return list;
		}

		private void Game(List<SkillPageLine> lines, Player player, float level)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			float num = FarmingPerks.Factor(level);
			lines.Add(new SkillPageLine(F("farm_stamina", FarmingPage.Percent(0.5f * num)), false, T("farm_stamina_title"), T("farm_stamina_tip")));
			string name;
			Attack val = HarvestAttack(player, out name);
			if (val == null)
			{
				lines.Add(new SkillPageLine(T("farm_scythe_any"), false, T("farm_scythe_title"), T("farm_scythe_any_tip")));
				return;
			}
			float value = Mathf.Lerp(val.m_harvestRadius, val.m_harvestRadiusMaxLevel, num);
			lines.Add(new SkillPageLine(F("farm_reach", name, Number(value)), false, name, T("farm_reach_tip")));
		}

		private static Attack? HarvestAttack(Player? player, out string name)
		{
			name = "";
			if (!Object.op_Implicit((Object)(object)player))
			{
				return null;
			}
			foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems())
			{
				Attack val = ((allItem.m_shared.m_attack != null && allItem.m_shared.m_attack.m_harvest) ? allItem.m_shared.m_attack : ((allItem.m_shared.m_secondaryAttack != null && allItem.m_shared.m_secondaryAttack.m_harvest) ? allItem.m_shared.m_secondaryAttack : null));
				if (val != null)
				{
					name = ((Localization.instance != null) ? Localization.instance.Localize(allItem.m_shared.m_name) : allItem.m_shared.m_name);
					return val;
				}
			}
			return null;
		}

		private void Perk(List<SkillPageLine> lines, float level, string name, int perkLevel, string tip)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			string text = FarmingPage.PerkLine(name, perkLevel, level, T("farm_unlocked"), T("farm_at_level"));
			if (text != null)
			{
				lines.Add(new SkillPageLine(text, false, name, tip));
			}
		}

		private string Own(bool on, string setting)
		{
			if (!on)
			{
				return " " + F("farm_switch_off", setting);
			}
			return "";
		}

		private static string Key(string token, string fallback)
		{
			if (Localization.instance == null)
			{
				return fallback;
			}
			return Localization.instance.Localize(token);
		}

		private string T(string key)
		{
			return text?.Text(key) ?? key;
		}

		private string F(string key, params object[] arguments)
		{
			return text?.Format(key, arguments) ?? key;
		}

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

		public void Dispose()
		{
			experience?.Dispose();
			experience = null;
			page?.Dispose();
			page = null;
			foreach (CustomRecipe recipe2 in recipes)
			{
				Recipe recipe = recipe2.Recipe;
				if (Object.op_Implicit((Object)(object)recipe))
				{
					recipe.m_enabled = false;
				}
				ItemManager.Instance.RemoveRecipe(recipe2);
			}
			recipes.Clear();
			recipesOn = false;
		}
	}
	internal static class FarmingExperience
	{
		private sealed class Scope
		{
			internal CropInfo? Crop;

			internal string? Discover;
		}

		[HarmonyPatch(typeof(Player), "UpdatePlacement")]
		private static class PlacementScope
		{
			private static readonly FieldRef<Player, GameObject> Ghost = AccessTools.FieldRefAccess<Player, GameObject>("m_placementGhost");

			private static void Prefix(Player __instance, out bool __state)
			{
				__state = false;
				if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Object.op_Implicit((Object)(object)Ghost.Invoke(__instance)) && FarmingState.Active)
				{
					Piece selectedPiece = __instance.GetSelectedPiece();
					if (Object.op_Implicit((Object)(object)selectedPiece) && Object.op_Implicit((Object)(object)((Component)selectedPiece).GetComponent<Plant>()))
					{
						__state = Begin(CropIndex.OfPlant(PlacementBackend.PrefabName(((Component)selectedPiece).gameObject)), null);
					}
				}
			}

			private static Exception? Finalizer(Exception? __exception, bool __state)
			{
				End(__state);
				return __exception;
			}
		}

		internal const string PickedKey = "wildroot.farm.picked";

		private static readonly Scope current = new Scope();

		private static Scope? scope;

		internal static bool Begin(CropInfo? crop, string? discover)
		{
			if (scope != null)
			{
				return false;
			}
			current.Crop = crop;
			current.Discover = discover;
			scope = current;
			return true;
		}

		internal static void End(bool opened)
		{
			if (opened)
			{
				scope = null;
			}
		}

		internal static float Scale(Player player, float factor)
		{
			FarmingRules rules = FarmingState.Rules;
			Scope scope = FarmingExperience.scope;
			if (!rules.Enabled || scope == null)
			{
				return factor;
			}
			bool flag = false;
			string discover = scope.Discover;
			scope.Discover = null;
			if (discover != null)
			{
				player.m_customData.TryGetValue("wildroot.farm.picked", out var value);
				var (flag2, value2) = FarmingXp.Discover(value, discover);
				if (flag2)
				{
					player.m_customData["wildroot.farm.picked"] = value2;
					flag = true;
				}
			}
			return factor * FarmingXp.Scale(rules, CropIndex.Value(scope.Crop), flag);
		}

		internal static void Planted(Player actor, Piece piece)
		{
			if (!Object.op_Implicit((Object)(object)actor) || (Object)(object)actor != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			bool opened = Begin(CropIndex.OfPlant(PlacementBackend.PrefabName(((Component)piece).gameObject)), null);
			try
			{
				((Character)actor).RaiseSkill((SkillType)106, 1f);
			}
			finally
			{
				End(opened);
			}
		}
	}
	internal static class PlanterMark
	{
		[HarmonyPatch(typeof(Player), "PlacePiece")]
		private static class Placing
		{
			private static void Prefix(Player __instance)
			{
				placing = (Object)(object)__instance == (Object)(object)Player.m_localPlayer;
			}

			private static Exception? Finalizer(Exception? __exception)
			{
				placing = false;
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Piece), "SetCreator")]
		private static class Creator
		{
			private static void Postfix(Piece __instance, long uid)
			{
				if (!placing || uid == 0L)
				{
					return;
				}
				PlantedPickable.Mark(__instance);
				if (FarmingState.Active)
				{
					Plant component = ((Component)__instance).GetComponent<Plant>();
					ZDO val = (Object.op_Implicit((Object)(object)component) ? PlantKeys.Of(component) : null);
					if (val != null && !GrowsVine(component))
					{
						PlantKeys.Write(val, uid, FarmingState.Level(Player.m_localPlayer));
					}
				}
			}
		}

		private static bool placing;

		private static bool GrowsVine(Plant plant)
		{
			if (plant.m_grownPrefabs == null)
			{
				return false;
			}
			GameObject[] grownPrefabs = plant.m_grownPrefabs;
			foreach (GameObject val in grownPrefabs)
			{
				if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.GetComponent<Vine>()))
				{
					return true;
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Plant), "GetGrowTime")]
	internal static class FarmingGrowTime
	{
		private static void Postfix(Plant __instance, ref float __result)
		{
			FarmingRules rules = FarmingState.Rules;
			if (rules.Enabled)
			{
				ZDO zdo = PlantKeys.Of(__instance);
				if (PlantKeys.IsPlanted(zdo))
				{
					__result = FarmingPerks.GrowTime(rules, __result, true, PlantKeys.Level(zdo));
				}
			}
		}
	}
	[HarmonyPatch(typeof(Plant), "UpdateHealth")]
	internal static class FarmingPlantHealth
	{
		private sealed class Mark
		{
			internal double Time;
		}

		private static readonly ConditionalWeakTable<Plant, Mark> marks = new ConditionalWeakTable<Plant, Mark>();

		private static void Prefix(Plant __instance)
		{
			CropInfo cropInfo = CropIndex.CropOf(__instance);
			if (cropInfo != null && Object.op_Implicit((Object)(object)cropInfo.Prefab))
			{
				FarmingRules rules = FarmingState.Rules;
				ZDO zdo = PlantKeys.Of(__instance);
				bool planted = rules.Enabled && PlantKeys.IsPlanted(zdo);
				float level = PlantKeys.Level(zdo);
				FarmingTraits.Apply(__instance, cropInfo.Prefab, rules, planted, level);
			}
		}

		private static void Postfix(Plant __instance, double timeSincePlanted)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			FarmingRules rules = FarmingState.Rules;
			if (!rules.Enabled || timeSincePlanted < 10.0 || !Object.op_Implicit((Object)(object)ZNet.instance))
			{
				return;
			}
			ZNetView val = FarmingState.PlantView.Invoke(__instance);
			if (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || !val.IsOwner())
			{
				return;
			}
			double timeSeconds = ZNet.instance.GetTimeSeconds();
			if (!marks.TryGetValue(__instance, out Mark value))
			{
				marks.Add(__instance, new Mark
				{
					Time = timeSeconds
				});
				return;
			}
			double num = timeSeconds - value.Time;
			if (!(num < 60.0))
			{
				value.Time = timeSeconds;
				ZDO zDO = val.GetZDO();
				double num2 = FarmingPerks.RainCredit(rules, num, EnvMan.IsWet() && (int)__instance.GetStatus() == 0 && PlantKeys.IsPlanted(zDO));
				if (!(num2 <= 0.0))
				{
					long num3 = zDO.GetLong(ZDOVars.s_plantTime, ZNet.instance.GetTime().Ticks);
					zDO.Set(ZDOVars.s_plantTime, num3 - TimeSpan.FromSeconds(num2).Ticks);
				}
			}
		}
	}
	internal static class FarmingTraits
	{
		internal static void Apply(Plant plant, Plant prefab, FarmingRules rules, bool planted, float level)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected I4, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected I4, but got Unknown
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			plant.m_growRadius = FarmingPerks.GrowRadius(rules, prefab.m_growRadius, planted, level);
			plant.m_tolerateHeat = FarmingPerks.HeatTolerant(rules, prefab.m_tolerateHeat, planted, level);
			plant.m_tolerateCold = FarmingPerks.ColdTolerant(rules, prefab.m_tolerateCold, (int)prefab.m_biome, planted, level);
			plant.m_biome = (Biome)FarmingPerks.Biomes(rules, (int)prefab.m_biome, planted, level);
		}

		internal static (float radius, Biome biome, bool heat, bool cold) Placement(Plant plant)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected I4, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected I4, but got Unknown
			ZNetView val = FarmingState.PlantView.Invoke(plant);
			FarmingRules rules = FarmingState.Rules;
			if ((Object.op_Implicit((Object)(object)val) && val.IsValid()) || !rules.Enabled || CropIndex.OfPlant(PlacementBackend.PrefabName(((Component)plant).gameObject)) == null)
			{
				return (radius: plant.m_growRadius, biome: plant.m_biome, heat: plant.m_tolerateHeat, cold: plant.m_tolerateCold);
			}
			float num = FarmingState.Level(Player.m_localPlayer);
			return (radius: FarmingPerks.GrowRadius(rules, plant.m_growRadius, true, num), biome: (Biome)FarmingPerks.Biomes(rules, (int)plant.m_biome, true, num), heat: FarmingPerks.HeatTolerant(rules, plant.m_tolerateHeat, true, num), cold: FarmingPerks.ColdTolerant(rules, plant.m_tolerateCold, (int)plant.m_biome, true, num));
		}
	}
	internal static class FarmingSeeds
	{
		internal static ItemDrop? Roll(FarmingRules rules, float level, CropInfo crop, Vector3 position)
		{
			//IL_003a: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			float num = FarmingPerks.SeedReturnChance(rules, level);
			if (!Object.op_Implicit((Object)(object)crop.Seed) || num <= 0f || (num < 1f && Random.value >= num))
			{
				return null;
			}
			GameObject val = Object.Instantiate<GameObject>(((Component)crop.Seed).gameObject, position + Vector3.up * 0.5f, Quaternion.Euler(0f, Random.Range(0f, 360f), 0f));
			ItemDrop component = val.GetComponent<ItemDrop>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return null;
			}
			component.SetStack(1);
			ItemDrop.OnCreateNew(component, false);
			ZNetView component2 = ((Component)component).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component2) && component2.IsValid())
			{
				ItemDrop.SaveToZDO(component.m_itemData, component2.GetZDO(), -1);
			}
			Rigidbody component3 = val.GetComponent<Rigidbody>();
			if (Object.op_Implicit((Object)(object)component3))
			{
				component3.linearVelocity = Vector3.up * 4f;
			}
			return component;
		}
	}
	[HarmonyPatch(typeof(Pickable), "Interact")]
	internal static class FarmingPick
	{
		internal sealed class State
		{
			internal Pickable Pickable;

			internal CropInfo Crop;

			internal Player Player;

			internal State? Previous;

			internal ZDOID Id;

			internal float Chance;

			internal bool Opened;

			internal bool Finished;

			internal bool Captured;
		}

		[ThreadStatic]
		private static State? ordinary;

		private static void Prefix(Pickable __instance, Humanoid character, out State? __state)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			__state = null;
			Player val = (Player)(object)((character is Player) ? character : null);
			if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer || !FarmingState.Active || !CanPick(__instance))
			{
				return;
			}
			CropInfo cropInfo = CropIndex.OfPickable(PlacementBackend.PrefabName(((Component)__instance).gameObject));
			if (cropInfo != null)
			{
				__state = new State
				{
					Pickable = __instance,
					Crop = cropInfo,
					Id = FarmingState.PickableView.Invoke(__instance).GetZDO().m_uid,
					Chance = __instance.m_maxLevelBonusChance
				};
				__instance.m_maxLevelBonusChance = FarmingPerks.BonusChance(FarmingState.Rules, __state.Chance);
				__state.Opened = FarmingExperience.Begin(cropInfo, cropInfo.Pickable);
				if (!NativeHarvestService.Dispatching)
				{
					__state.Player = val;
					__state.Previous = ordinary;
					ordinary = __state;
				}
			}
		}

		private static void Postfix(Pickable __instance, Humanoid character, State? __state)
		{
			if (__state != null)
			{
				Finish(__state);
			}
		}

		private static Exception? Finalizer(Exception? __exception, State? __state)
		{
			if (__state != null)
			{
				Finish(__state);
			}
			return __exception;
		}

		private static void Finish(State state)
		{
			if (!state.Finished)
			{
				state.Finished = true;
				if (ordinary == state)
				{
					ordinary = state.Previous;
				}
				if (Object.op_Implicit((Object)(object)state.Pickable))
				{
					state.Pickable.m_maxLevelBonusChance = state.Chance;
				}
				FarmingExperience.End(state.Opened);
			}
		}

		internal static bool CaptureDispatch(ZNetView view, string method, object[] arguments)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			State state = ordinary;
			if (state == null || state.Finished || method != "RPC_Pick" || (Object)(object)FarmingState.PickableView.Invoke(state.Pickable) != (Object)(object)view || arguments.Length != 1 || !(arguments[0] is int bonus))
			{
				return false;
			}
			if (state.Captured)
			{
				return true;
			}
			Plugin? instance = Plugin.Instance;
			state.Captured = instance != null && instance.Harvesting?.SubmitSingle(state.Pickable, state.Player, state.Id, bonus) == true;
			return state.Captured;
		}

		private static bool CanPick(Pickable pickable)
		{
			ZNetView val = FarmingState.PickableView.Invoke(pickable);
			if (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || FarmingState.PickableEnabled.Invoke(pickable) == 0 || FarmingState.PickablePicked.Invoke(pickable) || FarmingState.PickablePickedLocal.Invoke(pickable) || !pickable.CanBePicked())
			{
				return false;
			}
			Floating val2 = (pickable.m_tarPreventsPicking ? ((Component)pickable).GetComponent<Floating>() : null);
			if (Object.op_Implicit((Object)(object)val2))
			{
				return !val2.IsInTar();
			}
			return true;
		}
	}
	internal static class FarmingState
	{
		internal const SkillType Skill = (SkillType)106;

		private static readonly FarmingRules Off = new FarmingRules
		{
			Enabled = false
		};

		internal static readonly FieldRef<Plant, ZNetView> PlantView = AccessTools.FieldRefAccess<Plant, ZNetView>("m_nview");

		internal static readonly FieldRef<Pickable, ZNetView> PickableView = AccessTools.FieldRefAccess<Pickable, ZNetView>("m_nview");

		internal static readonly FieldRef<Pickable, bool> PickablePicked = AccessTools.FieldRefAccess<Pickable, bool>("m_picked");

		internal static readonly FieldRef<Pickable, bool> PickablePickedLocal = AccessTools.FieldRefAccess<Pickable, bool>("m_pickedLocal");

		internal static readonly FieldRef<Pickable, int> PickableEnabled = AccessTools.FieldRefAccess<Pickable, int>("m_enabled");

		internal static FarmingRules Rules
		{
			get
			{
				Plugin instance = Plugin.Instance;
				WildrootConfig wildrootConfig = (Object.op_Implicit((Object)(object)instance) ? instance.Settings : null);
				if (wildrootConfig != null && instance.GameplayAvailable && wildrootConfig.Active)
				{
					return wildrootConfig.Farming;
				}
				return Off;
			}
		}

		internal static bool Active => Rules.Enabled;

		internal static float Level(Player? player)
		{
			if (!Object.op_Implicit((Object)(object)player))
			{
				return 0f;
			}
			return MeadworksSkills.EffectiveLevel(player, (SkillType)106);
		}
	}
	internal static class PlantKeys
	{
		internal const string PlanterKey = "wildroot.farm.planter";

		internal const string LevelKey = "wildroot.farm.level";

		private static readonly int PlanterHash = StringExtensionMethods.GetStableHashCode("wildroot.farm.planter");

		private static readonly int LevelHash = StringExtensionMethods.GetStableHashCode("wildroot.farm.level");

		internal static ZDO? Of(Plant plant)
		{
			ZNetView val = (Object.op_Implicit((Object)(object)plant) ? FarmingState.PlantView.Invoke(plant) : null);
			if (!Object.op_Implicit((Object)(object)val) || !val.IsValid())
			{
				return null;
			}
			return val.GetZDO();
		}

		internal static bool IsPlanted(ZDO? zdo)
		{
			if (zdo != null)
			{
				return zdo.GetLong(PlanterHash, 0L) != 0;
			}
			return false;
		}

		internal static float Level(ZDO? zdo)
		{
			if (zdo == null)
			{
				return 0f;
			}
			return zdo.GetFloat(LevelHash, 0f);
		}

		interna