Decompiled source of Vaettir v1.6.3

plugins/Vaettir/Vaettir.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Ezomic.Core;
using Ezomic.Shared;
using Furrow;
using Grove;
using HarmonyLib;
using Hod;
using Microsoft.CodeAnalysis;
using Splatform;
using Stow;
using TMPro;
using Thicket;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("Thijssen Software")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Robbin Thijssen")]
[assembly: AssemblyDescription("Plant an ancient seed, feed it greydwarf blood, and a forest spirit answers.")]
[assembly: AssemblyFileVersion("1.6.3.0")]
[assembly: AssemblyInformationalVersion("1.6.3+2ad453647a802b04648a753118287c2d5c57699b")]
[assembly: AssemblyProduct("Vaettir")]
[assembly: AssemblyTitle("Vaettir")]
[assembly: AssemblyVersion("1.6.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Ezomic.Shared
{
	public static class Prefabs
	{
		private sealed class Kept
		{
			internal string Name;

			internal Func<GameObject> Build;

			internal bool Item;

			internal string Tool;

			internal GameObject Prefab;

			internal int Failures;

			internal bool Abandoned;
		}

		private static ManualLogSource _log;

		private static GameObject _holder;

		private static readonly List<Kept> Standing = new List<Kept>();

		private const int MaxFailures = 5;

		private static readonly HashSet<string> Complained = new HashSet<string>();

		private static FieldRef<ZNetScene, Dictionary<int, GameObject>> _named;

		private static MethodInfo _updateRegisters;

		public static ManualLogSource Log
		{
			get
			{
				return _log ?? (_log = Logger.CreateLogSource("Prefabs"));
			}
			set
			{
				_log = value;
			}
		}

		public static Transform Holder
		{
			get
			{
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Expected O, but got Unknown
				if ((Object)(object)_holder == (Object)null)
				{
					_holder = new GameObject(Log.SourceName + "Prefabs");
					_holder.SetActive(false);
					Object.DontDestroyOnLoad((Object)(object)_holder);
				}
				return _holder.transform;
			}
		}

		public static void Keep(string name, Func<GameObject> build, bool item = false, string buildTool = null)
		{
			if (string.IsNullOrEmpty(name))
			{
				throw new ArgumentNullException("name");
			}
			if (build == null)
			{
				throw new ArgumentNullException("build");
			}
			foreach (Kept item2 in Standing)
			{
				if (!(item2.Name != name))
				{
					item2.Build = build;
					item2.Item = item;
					item2.Tool = buildTool;
					item2.Failures = 0;
					item2.Abandoned = false;
					return;
				}
			}
			Standing.Add(new Kept
			{
				Name = name,
				Build = build,
				Item = item,
				Tool = buildTool
			});
			Declare(name);
			Log.LogInfo((object)("Keeping " + name + " registered."));
		}

		public static void Drop(string name)
		{
			Standing.RemoveAll((Kept k) => k.Name == name);
		}

		public static void Tick()
		{
			if (Standing.Count == 0)
			{
				return;
			}
			ZNetScene instance = ZNetScene.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				for (int i = 0; i < Standing.Count; i++)
				{
					Apply(Standing[i], instance);
				}
			}
		}

		private static void Apply(Kept kept, ZNetScene scene)
		{
			if (!kept.Abandoned && (!((Object)(object)kept.Prefab == (Object)null) || TryBuild(kept)))
			{
				if ((Object)(object)scene.GetPrefab(kept.Name) == (Object)null)
				{
					Register(kept.Prefab);
				}
				if (kept.Item)
				{
					RegisterItem(kept.Prefab);
				}
				if (!string.IsNullOrEmpty(kept.Tool))
				{
					AddToTool(kept.Prefab, kept.Tool);
				}
			}
		}

		private static bool TryBuild(Kept kept)
		{
			GameObject val = null;
			try
			{
				val = kept.Build();
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Building " + kept.Name + " failed: " + ex.Message));
			}
			if ((Object)(object)val != (Object)null)
			{
				kept.Prefab = val;
				kept.Failures = 0;
				if (((Object)val).name != kept.Name)
				{
					((Object)val).name = kept.Name;
				}
				return true;
			}
			if (++kept.Failures < 5)
			{
				return false;
			}
			kept.Abandoned = true;
			Log.LogError((object)(kept.Name + " could not be built after " + 5 + " attempts and will not be retried. Anything already built with it in a world is untouched; it simply will not be placeable this session."));
			return false;
		}

		public static bool Known(string name)
		{
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				return (Object)(object)ZNetScene.instance.GetPrefab(name) != (Object)null;
			}
			return false;
		}

		public static GameObject Clone(GameObject source, string name)
		{
			if ((Object)(object)source == (Object)null)
			{
				return null;
			}
			bool forceDisableInit = ZNetView.m_forceDisableInit;
			ZNetView.m_forceDisableInit = true;
			GameObject val;
			try
			{
				val = Object.Instantiate<GameObject>(source, Holder);
			}
			finally
			{
				ZNetView.m_forceDisableInit = forceDisableInit;
			}
			((Object)val).name = name;
			return val;
		}

		public static GameObject Donor(string commaSeparated, out string chosen)
		{
			chosen = null;
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return null;
			}
			string[] array = (commaSeparated ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0)
				{
					GameObject prefab = ZNetScene.instance.GetPrefab(text);
					if (!((Object)(object)prefab == (Object)null))
					{
						chosen = text;
						return prefab;
					}
				}
			}
			return null;
		}

		public static bool Register(GameObject prefab)
		{
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)prefab == (Object)null)
			{
				return false;
			}
			if ((Object)(object)instance.GetPrefab(((Object)prefab).name) != (Object)null)
			{
				return true;
			}
			if (!instance.m_prefabs.Contains(prefab))
			{
				instance.m_prefabs.Add(prefab);
			}
			try
			{
				NamedPrefabs(instance)[StringExtensionMethods.GetStableHashCode(((Object)prefab).name)] = prefab;
			}
			catch (Exception ex)
			{
				ComplainOnce("scene:" + ((Object)prefab).name, "Could not register " + ((Object)prefab).name + " with ZNetScene: " + ex.Message);
				return false;
			}
			Log.LogInfo((object)("Registered " + ((Object)prefab).name + " with ZNetScene."));
			return true;
		}

		public static bool RegisterItem(GameObject prefab)
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)prefab == (Object)null)
			{
				return false;
			}
			if (instance.m_items == null || instance.m_items.Count == 0)
			{
				return false;
			}
			if ((Object)(object)instance.GetItemPrefab(((Object)prefab).name) != (Object)null)
			{
				return true;
			}
			if (!instance.m_items.Contains(prefab))
			{
				instance.m_items.Add(prefab);
			}
			try
			{
				UpdateRegisters(instance);
			}
			catch (Exception ex)
			{
				ComplainOnce("db:" + ((Object)prefab).name, "Could not refresh ObjectDB for " + ((Object)prefab).name + ": " + ex.Message);
				return false;
			}
			Log.LogInfo((object)("Registered " + ((Object)prefab).name + " with ObjectDB."));
			return true;
		}

		public static PieceTable ToolPieces(string toolPrefab)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null || string.IsNullOrEmpty(toolPrefab))
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(toolPrefab);
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val == (Object)null || val.m_itemData == null || val.m_itemData.m_shared == null)
			{
				return null;
			}
			PieceTable buildPieces = val.m_itemData.m_shared.m_buildPieces;
			if (!((Object)(object)buildPieces != (Object)null) || buildPieces.m_pieces == null)
			{
				return null;
			}
			return buildPieces;
		}

		public static bool InTool(GameObject prefab, string toolPrefab)
		{
			PieceTable val = ToolPieces(toolPrefab);
			if ((Object)(object)val != (Object)null && (Object)(object)prefab != (Object)null)
			{
				return val.m_pieces.Contains(prefab);
			}
			return false;
		}

		public static bool AddToTool(GameObject prefab, string toolPrefab)
		{
			if ((Object)(object)prefab == (Object)null)
			{
				return false;
			}
			PieceTable val = ToolPieces(toolPrefab);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if (val.m_pieces.Contains(prefab))
			{
				return true;
			}
			val.m_pieces.Add(prefab);
			Log.LogInfo((object)(((Object)prefab).name + " added to the " + toolPrefab + "."));
			return true;
		}

		private static void ComplainOnce(string key, string message)
		{
			if (Complained.Add(key))
			{
				Log.LogError((object)message);
			}
		}

		private static void Declare(string name)
		{
			try
			{
				if (Chainloader.PluginInfos.ContainsKey("ezomic.valheim.core"))
				{
					Tell(name);
				}
			}
			catch (Exception)
			{
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void Tell(string name)
		{
			Suite.Owns(new string[1] { name });
		}

		private static Dictionary<int, GameObject> NamedPrefabs(ZNetScene scene)
		{
			if (_named == null)
			{
				_named = AccessTools.FieldRefAccess<ZNetScene, Dictionary<int, GameObject>>("m_namedPrefabs");
			}
			return _named.Invoke(scene);
		}

		private static void UpdateRegisters(ObjectDB db)
		{
			if (_updateRegisters == null)
			{
				_updateRegisters = AccessTools.Method(typeof(ObjectDB), "UpdateRegisters", (Type[])null, (Type[])null);
			}
			if (_updateRegisters == null)
			{
				throw new MissingMethodException("ObjectDB.UpdateRegisters");
			}
			_updateRegisters.Invoke(db, null);
		}
	}
	public static class BiomeIndex
	{
		private struct Placement
		{
			public int Index;

			public int Confidence;
		}

		private static ManualLogSource _log;

		public static Func<string, string> BiomeForKey;

		public static Func<string> Overrides;

		public static string OverrideSetting = "BiomeOverrides";

		public const string None = "none";

		public const string Everything = "all";

		private static readonly KeyValuePair<string, Biome>[] Ordered = new KeyValuePair<string, Biome>[9]
		{
			new KeyValuePair<string, Biome>("meadows", (Biome)1),
			new KeyValuePair<string, Biome>("blackforest", (Biome)8),
			new KeyValuePair<string, Biome>("ocean", (Biome)256),
			new KeyValuePair<string, Biome>("swamp", (Biome)2),
			new KeyValuePair<string, Biome>("mountain", (Biome)4),
			new KeyValuePair<string, Biome>("plains", (Biome)16),
			new KeyValuePair<string, Biome>("mistlands", (Biome)512),
			new KeyValuePair<string, Biome>("ashlands", (Biome)32),
			new KeyValuePair<string, Biome>("deepnorth", (Biome)64)
		};

		public static readonly string[] All = BuildNames();

		private const int Guessed = 0;

		private const int Derived = 1;

		private const int Pinned = 2;

		private static Dictionary<string, Placement> _biomeOf;

		private static bool _complete;

		private const int MaxClosurePasses = 12;

		private const int MaxFallbackRounds = 4;

		public static ManualLogSource Log
		{
			get
			{
				return _log ?? (_log = Logger.CreateLogSource("BiomeIndex"));
			}
			set
			{
				_log = value;
			}
		}

		public static bool Complete => _complete;

		public static int Count
		{
			get
			{
				if (_biomeOf != null)
				{
					return _biomeOf.Count;
				}
				return 0;
			}
		}

		private static string BossBiome(string globalKey)
		{
			return BiomeForKey?.Invoke(globalKey);
		}

		private static string[] BuildNames()
		{
			string[] array = new string[Ordered.Length + 1];
			for (int i = 0; i < Ordered.Length; i++)
			{
				array[i] = Ordered[i].Key;
			}
			array[Ordered.Length] = "none";
			return array;
		}

		public static void Invalidate()
		{
			_biomeOf = null;
			_complete = false;
		}

		public static string BiomeOf(string prefabName)
		{
			if (_biomeOf != null && _biomeOf.TryGetValue(prefabName, out var value))
			{
				return Ordered[value.Index].Key;
			}
			return "none";
		}

		public static void Prepare()
		{
			Build();
		}

		private static void Build()
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (_complete && _biomeOf != null)
			{
				return;
			}
			ZoneSystem instance = ZoneSystem.instance;
			SpawnSystem val = Object.FindObjectOfType<SpawnSystem>();
			ZNetScene instance2 = ZNetScene.instance;
			Dictionary<string, Placement> dictionary = new Dictionary<string, Placement>();
			if ((Object)(object)instance != (Object)null && instance.m_vegetation != null)
			{
				foreach (ZoneVegetation item in instance.m_vegetation)
				{
					if (item != null && item.m_enable)
					{
						Harvest(dictionary, item.m_prefab, Earliest(item.m_biome));
					}
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				HarvestSpawnLists(dictionary, val);
			}
			if ((Object)(object)instance2 != (Object)null && instance2.m_prefabs != null)
			{
				Bosses(dictionary, instance2);
			}
			ApplyOverrides(dictionary);
			List<string> guesses = new List<string>();
			for (int i = 0; i < 4; i++)
			{
				int deferred = 0;
				for (int j = 0; j < 12; j++)
				{
					bool flag = Craft(dictionary, permissive: false, out deferred, null);
					if ((Object)(object)instance2 != (Object)null && instance2.m_prefabs != null)
					{
						flag |= Convert(dictionary, instance2);
					}
					if (!flag)
					{
						break;
					}
				}
				if (deferred == 0 || !Craft(dictionary, permissive: true, out var _, guesses))
				{
					break;
				}
			}
			ApplyOverrides(dictionary);
			_biomeOf = dictionary;
			_complete = (Object)(object)instance != (Object)null && (Object)(object)val != (Object)null && (Object)(object)instance2 != (Object)null;
			if (_complete)
			{
				Log.LogInfo((object)("Biome index built: " + dictionary.Count + " item(s) placed."));
				ReportGuesses(dictionary, guesses);
				return;
			}
			List<string> list = new List<string>();
			if ((Object)(object)instance == (Object)null)
			{
				list.Add("ZoneSystem");
			}
			if ((Object)(object)val == (Object)null)
			{
				list.Add("SpawnSystem");
			}
			if ((Object)(object)instance2 == (Object)null)
			{
				list.Add("ZNetScene");
			}
			Log.LogInfo((object)("Biome index partial: " + dictionary.Count + " item(s) placed, still waiting on " + string.Join(" and ", list.ToArray()) + "."));
		}

		private static void ReportGuesses(Dictionary<string, Placement> found, List<string> guesses)
		{
			if (guesses == null || guesses.Count == 0)
			{
				return;
			}
			List<string> list = new List<string>(guesses.Count);
			foreach (string guess in guesses)
			{
				if (found.TryGetValue(guess, out var value) && value.Confidence == 0)
				{
					list.Add(guess);
				}
			}
			if (list.Count != 0)
			{
				list.Sort(StringComparer.Ordinal);
				Log.LogWarning((object)("Biome index: " + list.Count + " item(s) classified from an INCOMPLETE ingredient list, because the closure could not place everything their recipe asks for. Their biome is a best guess and " + OverrideSetting + " is the cure: " + string.Join(", ", list.ToArray()) + "."));
			}
		}

		private static void HarvestSpawnLists(Dictionary<string, Placement> found, SpawnSystem spawn)
		{
			if (spawn.m_spawnLists == null)
			{
				return;
			}
			HashSet<string> hashSet = new HashSet<string>();
			foreach (SpawnSystemList spawnList in spawn.m_spawnLists)
			{
				if ((Object)(object)spawnList == (Object)null || spawnList.m_spawners == null)
				{
					continue;
				}
				foreach (SpawnData spawner in spawnList.m_spawners)
				{
					if (Runs(spawner) && !Conditional(spawner))
					{
						hashSet.Add(((Object)spawner.m_prefab).name);
					}
				}
			}
			foreach (SpawnSystemList spawnList2 in spawn.m_spawnLists)
			{
				if ((Object)(object)spawnList2 == (Object)null || spawnList2.m_spawners == null)
				{
					continue;
				}
				foreach (SpawnData spawner2 in spawnList2.m_spawners)
				{
					if (Runs(spawner2))
					{
						Harvest(found, spawner2.m_prefab, SpawnIndex(spawner2, hashSet));
					}
				}
			}
		}

		private static bool Runs(SpawnData row)
		{
			if (row != null && row.m_enabled && !row.m_devDisabled)
			{
				return (Object)(object)row.m_prefab != (Object)null;
			}
			return false;
		}

		private static bool Conditional(SpawnData row)
		{
			if (string.IsNullOrEmpty(row.m_requiredGlobalKey) && string.IsNullOrEmpty(row.m_requiredPersistentEvent))
			{
				if (row.m_requiredEnvironments != null)
				{
					return row.m_requiredEnvironments.Count > 0;
				}
				return false;
			}
			return true;
		}

		private static int SpawnIndex(SpawnData row, HashSet<string> unconditional)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			int num = Earliest(row.m_biome);
			if (num < 0)
			{
				return -1;
			}
			if (!string.IsNullOrEmpty(row.m_requiredPersistentEvent))
			{
				return -1;
			}
			if (!Conditional(row))
			{
				return num;
			}
			if (unconditional.Contains(((Object)row.m_prefab).name))
			{
				return -1;
			}
			string requiredGlobalKey = row.m_requiredGlobalKey;
			if (string.IsNullOrEmpty(requiredGlobalKey))
			{
				return num;
			}
			string text = BossBiome(requiredGlobalKey);
			if (text == null)
			{
				return num;
			}
			int num2 = IndexOf(text);
			if (num2 < 0)
			{
				return num;
			}
			if (num <= num2)
			{
				return num2;
			}
			return num;
		}

		private static void Harvest(Dictionary<string, Placement> found, GameObject prefab, int index)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)prefab == (Object)null || index < 0)
			{
				return;
			}
			Pickable[] componentsInChildren = prefab.GetComponentsInChildren<Pickable>(true);
			foreach (Pickable val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null)
				{
					Record(found, val.m_itemPrefab, index, 1);
				}
			}
			PickableItem[] componentsInChildren2 = prefab.GetComponentsInChildren<PickableItem>(true);
			foreach (PickableItem val2 in componentsInChildren2)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				if ((Object)(object)val2.m_itemPrefab != (Object)null)
				{
					Record(found, ((Component)val2.m_itemPrefab).gameObject, index, 1);
				}
				if (val2.m_randomItemPrefabs == null)
				{
					continue;
				}
				RandomItem[] randomItemPrefabs = val2.m_randomItemPrefabs;
				foreach (RandomItem val3 in randomItemPrefabs)
				{
					if ((Object)(object)val3.m_itemPrefab != (Object)null)
					{
						Record(found, ((Component)val3.m_itemPrefab).gameObject, index, 1);
					}
				}
			}
			MineRock5[] componentsInChildren3 = prefab.GetComponentsInChildren<MineRock5>(true);
			foreach (MineRock5 val4 in componentsInChildren3)
			{
				if ((Object)(object)val4 != (Object)null)
				{
					Record(found, val4.m_dropItems, index, 1);
				}
			}
			MineRock[] componentsInChildren4 = prefab.GetComponentsInChildren<MineRock>(true);
			foreach (MineRock val5 in componentsInChildren4)
			{
				if ((Object)(object)val5 != (Object)null)
				{
					Record(found, val5.m_dropItems, index, 1);
				}
			}
			DropOnDestroyed[] componentsInChildren5 = prefab.GetComponentsInChildren<DropOnDestroyed>(true);
			foreach (DropOnDestroyed val6 in componentsInChildren5)
			{
				if ((Object)(object)val6 != (Object)null)
				{
					Record(found, val6.m_dropWhenDestroyed, index, 1);
				}
			}
			LootSpawner[] componentsInChildren6 = prefab.GetComponentsInChildren<LootSpawner>(true);
			foreach (LootSpawner val7 in componentsInChildren6)
			{
				if ((Object)(object)val7 != (Object)null)
				{
					Record(found, val7.m_items, index, 1);
				}
			}
			Container[] componentsInChildren7 = prefab.GetComponentsInChildren<Container>(true);
			foreach (Container val8 in componentsInChildren7)
			{
				if ((Object)(object)val8 != (Object)null)
				{
					Record(found, val8.m_defaultItems, index, 1);
				}
			}
			CharacterDrop[] componentsInChildren8 = prefab.GetComponentsInChildren<CharacterDrop>(true);
			foreach (CharacterDrop drop in componentsInChildren8)
			{
				RecordDrops(found, drop, index);
			}
			SpawnArea[] componentsInChildren9 = prefab.GetComponentsInChildren<SpawnArea>(true);
			foreach (SpawnArea val9 in componentsInChildren9)
			{
				if ((Object)(object)val9 == (Object)null || val9.m_prefabs == null)
				{
					continue;
				}
				foreach (SpawnData prefab2 in val9.m_prefabs)
				{
					if (prefab2 != null && !((Object)(object)prefab2.m_prefab == (Object)null))
					{
						componentsInChildren8 = prefab2.m_prefab.GetComponentsInChildren<CharacterDrop>(true);
						foreach (CharacterDrop drop2 in componentsInChildren8)
						{
							RecordDrops(found, drop2, index);
						}
					}
				}
			}
			Plant[] componentsInChildren10 = prefab.GetComponentsInChildren<Plant>(true);
			foreach (Plant val10 in componentsInChildren10)
			{
				if ((Object)(object)val10 == (Object)null || val10.m_grownPrefabs == null)
				{
					continue;
				}
				GameObject[] grownPrefabs = val10.m_grownPrefabs;
				foreach (GameObject val11 in grownPrefabs)
				{
					if ((Object)(object)val11 == (Object)null)
					{
						continue;
					}
					componentsInChildren = val11.GetComponentsInChildren<Pickable>(true);
					foreach (Pickable val12 in componentsInChildren)
					{
						if ((Object)(object)val12 != (Object)null)
						{
							Record(found, val12.m_itemPrefab, index, 1);
						}
					}
					componentsInChildren8 = val11.GetComponentsInChildren<CharacterDrop>(true);
					foreach (CharacterDrop drop3 in componentsInChildren8)
					{
						RecordDrops(found, drop3, index);
					}
				}
			}
			TreeBase[] componentsInChildren11 = prefab.GetComponentsInChildren<TreeBase>(true);
			foreach (TreeBase val13 in componentsInChildren11)
			{
				if ((Object)(object)val13 == (Object)null)
				{
					continue;
				}
				Record(found, val13.m_dropWhenDestroyed, index, 1);
				if ((Object)(object)val13.m_stubPrefab != (Object)null)
				{
					componentsInChildren5 = val13.m_stubPrefab.GetComponentsInChildren<DropOnDestroyed>(true);
					foreach (DropOnDestroyed val14 in componentsInChildren5)
					{
						if ((Object)(object)val14 != (Object)null)
						{
							Record(found, val14.m_dropWhenDestroyed, index, 1);
						}
					}
				}
				HarvestLogChain(found, val13.m_logPrefab, index);
			}
		}

		private static void HarvestLogChain(Dictionary<string, Placement> found, GameObject log, int index)
		{
			if ((Object)(object)log == (Object)null)
			{
				return;
			}
			HashSet<GameObject> hashSet = new HashSet<GameObject>();
			Queue<GameObject> queue = new Queue<GameObject>();
			queue.Enqueue(log);
			while (queue.Count > 0)
			{
				GameObject val = queue.Dequeue();
				if ((Object)(object)val == (Object)null || !hashSet.Add(val))
				{
					continue;
				}
				TreeLog[] componentsInChildren = val.GetComponentsInChildren<TreeLog>(true);
				foreach (TreeLog val2 in componentsInChildren)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						Record(found, val2.m_dropWhenDestroyed, index, 1);
						if ((Object)(object)val2.m_subLogPrefab != (Object)null)
						{
							queue.Enqueue(val2.m_subLogPrefab);
						}
					}
				}
			}
		}

		private static void RecordDrops(Dictionary<string, Placement> found, CharacterDrop drop, int index)
		{
			if ((Object)(object)drop == (Object)null || drop.m_drops == null)
			{
				return;
			}
			foreach (Drop drop2 in drop.m_drops)
			{
				if (drop2 != null)
				{
					Record(found, drop2.m_prefab, index, 1);
				}
			}
		}

		private static bool Convert(Dictionary<string, Placement> found, ZNetScene scene)
		{
			bool flag = false;
			foreach (GameObject prefab in scene.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				Smelter component = prefab.GetComponent<Smelter>();
				if ((Object)(object)component != (Object)null && component.m_conversion != null)
				{
					foreach (ItemConversion item in component.m_conversion)
					{
						flag |= Inherit(found, item.m_from, item.m_to);
					}
				}
				CookingStation component2 = prefab.GetComponent<CookingStation>();
				if ((Object)(object)component2 != (Object)null && component2.m_conversion != null)
				{
					foreach (ItemConversion item2 in component2.m_conversion)
					{
						flag |= Inherit(found, item2.m_from, item2.m_to);
					}
				}
				Fermenter component3 = prefab.GetComponent<Fermenter>();
				if (!((Object)(object)component3 != (Object)null) || component3.m_conversion == null)
				{
					continue;
				}
				foreach (ItemConversion item3 in component3.m_conversion)
				{
					flag |= Inherit(found, item3.m_from, item3.m_to);
				}
			}
			return flag;
		}

		private static void Bosses(Dictionary<string, Placement> found, ZNetScene scene)
		{
			foreach (GameObject prefab in scene.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				Character component = prefab.GetComponent<Character>();
				if ((Object)(object)component == (Object)null || string.IsNullOrEmpty(component.m_defeatSetGlobalKey))
				{
					continue;
				}
				string text = BossBiome(component.m_defeatSetGlobalKey);
				if (text == null)
				{
					continue;
				}
				int num = IndexOf(text);
				if (num >= 0)
				{
					CharacterDrop[] componentsInChildren = prefab.GetComponentsInChildren<CharacterDrop>(true);
					foreach (CharacterDrop drop in componentsInChildren)
					{
						RecordDrops(found, drop, num);
					}
				}
			}
		}

		private static int IndexOf(string biome)
		{
			for (int i = 0; i < Ordered.Length; i++)
			{
				if (Ordered[i].Key == biome)
				{
					return i;
				}
			}
			return -1;
		}

		private static bool Craft(Dictionary<string, Placement> found, bool permissive, out int deferred, List<string> guesses)
		{
			deferred = 0;
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || instance.m_recipes == null)
			{
				return false;
			}
			bool result = false;
			foreach (Recipe recipe in instance.m_recipes)
			{
				if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null || recipe.m_resources == null || (Object)(object)((Component)recipe.m_item).gameObject == (Object)null)
				{
					continue;
				}
				int num = 0;
				int num2 = 0;
				int num3 = -1;
				int num4 = int.MaxValue;
				int num5 = 1;
				Requirement[] resources = recipe.m_resources;
				foreach (Requirement val in resources)
				{
					if (val == null || (Object)(object)val.m_resItem == (Object)null || (Object)(object)((Component)val.m_resItem).gameObject == (Object)null)
					{
						continue;
					}
					num++;
					if (found.TryGetValue(((Object)((Component)val.m_resItem).gameObject).name, out var value))
					{
						num2++;
						if (value.Index > num3)
						{
							num3 = value.Index;
						}
						if (value.Index < num4)
						{
							num4 = value.Index;
						}
						if (value.Confidence < num5)
						{
							num5 = value.Confidence;
						}
					}
				}
				if (num2 == 0)
				{
					continue;
				}
				bool requireOnlyOneIngredient = recipe.m_requireOnlyOneIngredient;
				int index = (requireOnlyOneIngredient ? num4 : num3);
				bool flag = false;
				if (!requireOnlyOneIngredient && num2 < num)
				{
					deferred++;
					if (!permissive)
					{
						continue;
					}
					num5 = 0;
					flag = true;
				}
				if (Record(found, ((Object)((Component)recipe.m_item).gameObject).name, index, num5))
				{
					result = true;
					if (flag && guesses != null && !guesses.Contains(((Object)((Component)recipe.m_item).gameObject).name))
					{
						guesses.Add(((Object)((Component)recipe.m_item).gameObject).name);
					}
				}
			}
			return result;
		}

		private static bool Inherit(Dictionary<string, Placement> found, ItemDrop from, ItemDrop to)
		{
			if ((Object)(object)from == (Object)null || (Object)(object)to == (Object)null || (Object)(object)((Component)from).gameObject == (Object)null || (Object)(object)((Component)to).gameObject == (Object)null)
			{
				return false;
			}
			if (!found.TryGetValue(((Object)((Component)from).gameObject).name, out var value))
			{
				return false;
			}
			int confidence = ((value.Confidence >= 1) ? 1 : value.Confidence);
			return Record(found, ((Object)((Component)to).gameObject).name, value.Index, confidence);
		}

		private static void ApplyOverrides(Dictionary<string, Placement> found)
		{
			string[] array = (Overrides?.Invoke() ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0)
				{
					continue;
				}
				string[] array2 = text.Split(new char[1] { ':' });
				if (array2.Length != 2)
				{
					Log.LogWarning((object)("Ignoring biome override '" + text + "': expected prefab:biome."));
					continue;
				}
				string text2 = array2[1].Trim().ToLowerInvariant();
				int num = IndexOf(text2);
				if (num < 0)
				{
					Log.LogWarning((object)("Ignoring biome override '" + text + "': '" + text2 + "' is not a biome."));
				}
				else
				{
					found[array2[0].Trim()] = new Placement
					{
						Index = num,
						Confidence = 2
					};
				}
			}
		}

		private static void Record(Dictionary<string, Placement> found, DropTable table, int index, int confidence)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if (table == null || table.m_drops == null)
			{
				return;
			}
			foreach (DropData drop in table.m_drops)
			{
				if ((Object)(object)drop.m_item != (Object)null)
				{
					Record(found, ((Object)drop.m_item).name, index, confidence);
				}
			}
		}

		private static void Record(Dictionary<string, Placement> found, GameObject prefab, int index, int confidence)
		{
			if ((Object)(object)prefab != (Object)null)
			{
				Record(found, ((Object)prefab).name, index, confidence);
			}
		}

		private static bool Record(Dictionary<string, Placement> found, string prefabName, int index, int confidence)
		{
			if (string.IsNullOrEmpty(prefabName) || index < 0 || index >= Ordered.Length)
			{
				return false;
			}
			if (found.TryGetValue(prefabName, out var value))
			{
				if (confidence < value.Confidence)
				{
					return false;
				}
				if (confidence == value.Confidence && index >= value.Index)
				{
					return false;
				}
			}
			found[prefabName] = new Placement
			{
				Index = index,
				Confidence = confidence
			};
			return true;
		}

		private static int Earliest(Biome mask)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < Ordered.Length; i++)
			{
				if ((mask & Ordered[i].Value) != 0)
				{
					return i;
				}
			}
			return -1;
		}
	}
}
namespace Thicket
{
	[HarmonyPatch]
	internal static class Carry
	{
		private static WildPlant _held;

		private static GameObject _visual;

		private static FieldRef<Humanoid, VisEquipment> _visRef;

		private static FieldRef<VisEquipment, GameObject> _rightInstanceRef;

		private static bool _visBindTried;

		internal static bool Carrying => _held != null;

		private static GameObject DrawnTool(Player player)
		{
			if (!_visBindTried)
			{
				_visBindTried = true;
				try
				{
					_visRef = AccessTools.FieldRefAccess<Humanoid, VisEquipment>("m_visEquipment");
					_rightInstanceRef = AccessTools.FieldRefAccess<VisEquipment, GameObject>("m_rightItemInstance");
				}
				catch (Exception ex)
				{
					GrovePlugin.Log.LogWarning((object)("No tool mount, using the fallback: " + ex.Message));
				}
			}
			if (_visRef == null || _rightInstanceRef == null)
			{
				return null;
			}
			VisEquipment val = _visRef.Invoke((Humanoid)(object)player);
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return _rightInstanceRef.Invoke(val);
		}

		internal static void Begin(Player player, WildPlant plant, bool picked)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: 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_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: 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_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || plant == null)
			{
				return;
			}
			_held = plant;
			_visual = WildPrefab.CarryVisual(plant, picked);
			if ((Object)(object)_visual != (Object)null)
			{
				Transform transform = ((Component)player).transform;
				Vector3 position = ((Component)player).transform.position + ((Component)player).transform.forward * 0.4f + Vector3.up * 1.5f;
				GameObject val = DrawnTool(player);
				if ((Object)(object)val != (Object)null)
				{
					transform = val.transform;
					Vector3 val2 = Vector3.zero;
					float num = 0f;
					MeshFilter[] componentsInChildren = val.GetComponentsInChildren<MeshFilter>();
					foreach (MeshFilter val3 in componentsInChildren)
					{
						if ((Object)(object)val3 == (Object)null || (Object)(object)val3.sharedMesh == (Object)null)
						{
							continue;
						}
						Bounds bounds = val3.sharedMesh.bounds;
						for (int j = 0; j < 8; j++)
						{
							Vector3 val4 = ((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3(((j & 1) == 0) ? (-1f) : 1f, ((j & 2) == 0) ? (-1f) : 1f, ((j & 4) == 0) ? (-1f) : 1f));
							Vector3 val5 = val.transform.InverseTransformPoint(((Component)val3).transform.TransformPoint(val4));
							if (((Vector3)(ref val5)).sqrMagnitude > num)
							{
								num = ((Vector3)(ref val5)).sqrMagnitude;
								val2 = val5;
							}
						}
					}
					position = ((num > 0f) ? (val.transform.TransformPoint(val2) + Vector3.up * 0.15f) : (((Component)player).transform.position + ((Component)player).transform.forward * 0.4f + Vector3.up * 1.5f));
				}
				_visual.transform.position = position;
				_visual.transform.rotation = ((Component)player).transform.rotation;
				_visual.transform.SetParent(transform, true);
			}
			((Character)player).Message((MessageType)2, "Carrying " + plant.Title + " - click open ground where its kind grows to plant it, or press R to set it down right here", 0, (Sprite)null, false);
		}

		internal static void Tick()
		{
			//IL_002c: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			if (_held == null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				Drop(null);
			}
			else if (((Character)localPlayer).IsDead())
			{
				PlantAt(((Component)localPlayer).transform.position, ignoreBiome: true);
			}
			else if (ZInput.GetButtonDown("Hide") || ZInput.GetButtonDown("JoyHide"))
			{
				PlantAt(((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward * 1.1f, ignoreBiome: true);
			}
			else
			{
				if (!ZInput.GetButtonDown("Use") && !ZInput.GetButtonDown("JoyUse"))
				{
					return;
				}
				GameCamera instance = GameCamera.instance;
				if (!((Object)(object)instance == (Object)null))
				{
					RaycastHit val = default(RaycastHit);
					if (Physics.Raycast(new Ray(((Component)instance).transform.position, ((Component)instance).transform.forward), ref val, 8f, LayerMask.GetMask(new string[3] { "terrain", "Default", "static_solid" })) && Vector3.Distance(((RaycastHit)(ref val)).point, ((Component)localPlayer).transform.position) < 6f && ((RaycastHit)(ref val)).normal.y > 0.5f)
					{
						PlantAt(((RaycastHit)(ref val)).point);
					}
					else
					{
						((Character)localPlayer).Message((MessageType)2, "No open ground there", 0, (Sprite)null, false);
					}
				}
			}
		}

		internal static void TryPlantFromTool(Player player)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			GameCamera instance = GameCamera.instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)player == (Object)null))
			{
				RaycastHit val = default(RaycastHit);
				if (Physics.Raycast(new Ray(((Component)instance).transform.position, ((Component)instance).transform.forward), ref val, 8f, LayerMask.GetMask(new string[3] { "terrain", "Default", "static_solid" })) && Vector3.Distance(((RaycastHit)(ref val)).point, ((Component)player).transform.position) < 6f && ((RaycastHit)(ref val)).normal.y > 0.5f)
				{
					PlantAt(((RaycastHit)(ref val)).point);
				}
				else
				{
					((Character)player).Message((MessageType)2, "No open ground there", 0, (Sprite)null, false);
				}
			}
		}

		private static void PlantAt(Vector3 where, bool ignoreBiome = false)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//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_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			Biome val = Heightmap.FindBiome(where);
			if (!ignoreBiome && (int)_held.Biomes != 0 && (_held.Biomes & val) == 0)
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null)
				{
					((Character)localPlayer).Message((MessageType)2, _held.Title + " does not grow here - it wants " + ((object)Unsafe.As<Biome, Biome>(ref _held.Biomes)/*cast due to .constrained prefix*/).ToString(), 0, (Sprite)null, false);
				}
				return;
			}
			GameObject val2 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(_held.Grown) : null);
			if ((Object)(object)val2 == (Object)null)
			{
				GrovePlugin.LogOnce(_held.Grown + " did not resolve; still carrying.");
				return;
			}
			Quaternion val3 = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f);
			Pickable val4 = default(Pickable);
			if (Object.Instantiate<GameObject>(val2, where, val3).TryGetComponent<Pickable>(ref val4))
			{
				val4.SetPicked(true);
			}
			Player localPlayer2 = Player.m_localPlayer;
			if ((Object)(object)localPlayer2 != (Object)null)
			{
				((Character)localPlayer2).Message((MessageType)1, _held.Title + " planted", 0, (Sprite)null, false);
				((Character)localPlayer2).RaiseSkill((SkillType)106, 1f);
			}
			Drop(null);
		}

		private static void Drop(string reason)
		{
			if ((Object)(object)_visual != (Object)null)
			{
				Object.Destroy((Object)(object)_visual);
			}
			_visual = null;
			_held = null;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Character), "SetRun")]
		private static void NoRunning(Character __instance, ref bool run)
		{
			if (Carrying && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				run = false;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Character), "Jump")]
		private static bool NoJumping(Character __instance)
		{
			if (Carrying)
			{
				return !((Object)(object)__instance == (Object)(object)Player.m_localPlayer);
			}
			return true;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Humanoid), "StartAttack")]
		private static bool NoAttacking(Humanoid __instance, ref bool __result)
		{
			if (!Carrying || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			__result = false;
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "UseHotbarItem")]
		private static bool NoHotbar(Player __instance)
		{
			if (Carrying)
			{
				return !((Object)(object)__instance == (Object)(object)Player.m_localPlayer);
			}
			return true;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Humanoid), "EquipItem", new Type[]
		{
			typeof(ItemData),
			typeof(bool)
		})]
		private static bool NoEquipping(Humanoid __instance, ref bool __result)
		{
			if (!Carrying || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			__result = false;
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Game), "Logout")]
		private static void PlantBeforeLogout()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			if (!Carrying)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				Drop(null);
				return;
			}
			PlantAt(((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward * 1.1f, ignoreBiome: true);
			if (Carrying)
			{
				Drop(null);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Humanoid), "HideHandItems")]
		private static bool NoSheathing(Humanoid __instance)
		{
			if (Carrying)
			{
				return !((Object)(object)__instance == (Object)(object)Player.m_localPlayer);
			}
			return true;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "Interact")]
		private static bool NoInteracting(Player __instance)
		{
			if (Carrying)
			{
				return !((Object)(object)__instance == (Object)(object)Player.m_localPlayer);
			}
			return true;
		}
	}
	internal static class SkillGate
	{
		private static readonly FieldRef<Player, PieceTable> BuildPiecesRef = AccessTools.FieldRefAccess<Player, PieceTable>("m_buildPieces");

		private static readonly FieldRef<Player, PlacementStatus> StatusRef = AccessTools.FieldRefAccess<Player, PlacementStatus>("m_placementStatus");

		private static readonly Action<Player, bool> SetGhostValid = AccessTools.MethodDelegate<Action<Player, bool>>(AccessTools.Method(typeof(Player), "SetPlacementGhostValid", (Type[])null, (Type[])null), (object)null, true);

		private static float _lastSaid;

		private static float Level(Player player)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			PieceTable val = BuildPiecesRef.Invoke(player);
			SkillType val2 = (SkillType)((!((Object)(object)val != (Object)null) || (int)val.m_skill == 0) ? 106 : ((int)val.m_skill));
			return ((Character)player).GetSkillFactor(val2) * 100f;
		}

		private static WildPlant Locked(Player player, Piece piece)
		{
			if (!ThicketConfig.Enabled.Value)
			{
				return null;
			}
			WildPlant wildPlant = WildPlants.Of(piece);
			if (wildPlant == null || wildPlant.Level <= 0)
			{
				return null;
			}
			if (!(Level(player) < (float)wildPlant.Level))
			{
				return null;
			}
			return wildPlant;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Piece),
			typeof(RequirementMode)
		})]
		private static void Gate(Player __instance, Piece piece, RequirementMode mode, ref bool __result)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if (__result && (int)mode != 1 && Locked(__instance, piece) != null)
			{
				__result = false;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
		private static void Ghost(Player __instance)
		{
			PieceTable val = BuildPiecesRef.Invoke(__instance);
			Piece val2 = (((Object)(object)val != (Object)null) ? val.GetSelectedPiece() : null);
			if (!((Object)(object)val2 == (Object)null) && Locked(__instance, val2) != null)
			{
				StatusRef.Invoke(__instance) = (PlacementStatus)1;
				if (SetGhostValid != null)
				{
					SetGhostValid(__instance, arg2: false);
				}
			}
		}

		public static void Tick(Player player)
		{
			if (!ThicketConfig.Enabled.Value || !ThicketConfig.SayTheLevel.Value || (Object)(object)player == (Object)null || !((Character)player).InPlaceMode() || (!ZInput.GetButtonDown("Attack") && !ZInput.GetButtonDown("JoyPlace")) || Time.time - _lastSaid < 0.5f)
			{
				return;
			}
			PieceTable val = BuildPiecesRef.Invoke(player);
			Piece val2 = (((Object)(object)val != (Object)null) ? val.GetSelectedPiece() : null);
			if (!((Object)(object)val2 == (Object)null))
			{
				WildPlant wildPlant = Locked(player, val2);
				if (wildPlant != null)
				{
					_lastSaid = Time.time;
					((Character)player).Message((MessageType)2, wildPlant.Title + " needs Farming " + wildPlant.Level + " ( you have " + Mathf.FloorToInt(Level(player)) + " )", 0, (Sprite)null, false);
				}
			}
		}
	}
	internal static class ThicketConfig
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<string> Donor;

		public static ConfigEntry<float> Scale;

		public static ConfigEntry<float> SpacingScale;

		public static ConfigEntry<bool> SayTheLevel;

		public static ConfigEntry<float> DigReach;

		public static ConfigEntry<float> DigAssist;

		public static ConfigEntry<bool> Verbose;

		private static readonly Dictionary<string, ConfigEntry<string>> Rows = new Dictionary<string, ConfigEntry<string>>();

		private const string Format = "Farming level | cost | biomes | seconds to take root (min-max).\nLevel 0 means no gate. Biomes are Valheim's own names, comma separated. The grow time is a range and each seedling picks its own point in it from its ZDO seed, so a row planted together does not come up all at once.";

		public static void Bind(ConfigFile config)
		{
			Enabled = config.Bind<bool>("Thicket", "Enabled", true, "Move the things the game does not let you move: dig wild berry bushes, thistle, dandelion and mushrooms up with the cultivator and replant them where you want them. Off and none of the pieces or items are registered at all - which is safe to do only before any have been replanted or dug up, because a prefab that no longer resolves takes every standing seedling and every uprooted item in a chest with it.");
			Donor = config.Bind<string>("Thicket", "Donor", "sapling_carrot", "The vanilla plant each seedling is cloned from, for its Piece, its ZNetView, its WearNTear and its Plant. Unlike the ancient sapling the donor's Plant is kept and reconfigured rather than torn out: an ordinary plant growing on an ordinary timer is exactly what is wanted here, so riding the game's own component is both less code and one less thing to break on an update.");
			Scale = config.Bind<float>("Thicket", "Scale", 1f, "Scale of the seedling in the ground. The grown bush is the game's own and is not affected.");
			SpacingScale = config.Bind<float>("Thicket", "SpacingScale", 1f, "Multiplier on how much clear ground each seedling demands around it. The base figure comes from the plant's own size - a bush needs metres where a mushroom needs centimetres - and this scales all of them at once. Below 1 packs them tighter than the grown bushes will fit.");
			DigReach = config.Bind<float>("Thicket", "DigReach", 6f, "How far away a wild plant can be dug up, in metres, measured from you rather than from the camera. Roughly the reach of any other click on the world.");
			DigAssist = config.Bind<float>("Thicket", "DigAssist", 12f, "How many degrees off the crosshair a plant may sit and still be dug, when the crosshair itself hits nothing. Wild plants are ragged and several have a collider narrower than they look, so an exact ray is a harder shot than the plant appears to be. The plant nearest the aim line wins, not the nearest plant. 0 turns the help off.");
			SayTheLevel = config.Bind<bool>("Thicket", "SayTheLevel", true, "Write the Farming level a plant needs into its build-menu description, and say it on screen when you try to plant one you have not earned. Without this the piece is simply greyed out, which reads as missing materials and sends you looking through your chests for berries you are already carrying.");
			Verbose = config.Bind<bool>("Thicket", "Verbose", false, "Log every row as it is parsed, and every prefab that could not be found.");
		}

		public static ConfigEntry<string> Row(ConfigFile config, WildPlant plant)
		{
			ConfigEntry<string> val = config.Bind<string>("Plants", plant.Id, plant.DefaultRow, plant.Note + "\nFarming level | cost | biomes | seconds to take root (min-max).\nLevel 0 means no gate. Biomes are Valheim's own names, comma separated. The grow time is a range and each seedling picks its own point in it from its ZDO seed, so a row planted together does not come up all at once.");
			Rows[plant.Id] = val;
			return val;
		}

		public static string RowFor(string id)
		{
			if (!Rows.TryGetValue(id, out var value))
			{
				return null;
			}
			return value.Value;
		}

		public static void Say(ManualLogSource log, string message)
		{
			if (Verbose.Value)
			{
				log.LogInfo((object)("Thicket: " + message));
			}
		}
	}
	[HarmonyPatch]
	internal static class Transplant
	{
		private static readonly FieldRef<Pickable, bool> PickedRef = AccessTools.FieldRefAccess<Pickable, bool>("m_picked");

		private static WildPlant Of(Pickable pickable)
		{
			if (!ThicketConfig.Enabled.Value || (Object)(object)pickable == (Object)null)
			{
				return null;
			}
			return WildPlants.OfGrown(Utils.GetPrefabName(((Object)((Component)pickable).gameObject).name));
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "TryPlacePiece")]
		private static bool Click(Player __instance, Piece piece, ref bool __result)
		{
			if ((Object)(object)piece == (Object)null || ((Object)((Component)piece).gameObject).name != "thicket_transplant")
			{
				return true;
			}
			__result = false;
			if (Carry.Carrying)
			{
				Carry.TryPlantFromTool(__instance);
				return false;
			}
			Pickable stranger;
			Pickable pickable = UnderCrosshair(__instance, out stranger);
			WildPlant wildPlant = Of(pickable);
			if (wildPlant == null)
			{
				((Character)__instance).Message((MessageType)2, ((Object)(object)stranger != (Object)null) ? (Humanise(stranger) + " cannot be dug up") : "Nothing here to dig up", 0, (Sprite)null, false);
				return false;
			}
			Dig(__instance, pickable, wildPlant);
			return false;
		}

		private static Pickable UnderCrosshair(Player player, out Pickable stranger)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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_0043: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: 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_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			stranger = null;
			GameCamera instance = GameCamera.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			float num = Mathf.Max(1f, ThicketConfig.DigReach.Value);
			Vector3 position = ((Component)instance).transform.position;
			Vector3 forward = ((Component)instance).transform.forward;
			RaycastHit[] array = Physics.RaycastAll(new Ray(position, forward), num + 6f);
			Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
			RaycastHit[] array2 = array;
			for (int num2 = 0; num2 < array2.Length; num2++)
			{
				RaycastHit val = array2[num2];
				if ((Object)(object)((RaycastHit)(ref val)).collider == (Object)null)
				{
					continue;
				}
				Pickable componentInParent = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<Pickable>();
				if (!((Object)(object)componentInParent == (Object)null) && !(Vector3.Distance(((Component)componentInParent).transform.position, ((Component)player).transform.position) > num))
				{
					if (Of(componentInParent) != null)
					{
						return componentInParent;
					}
					if ((Object)(object)stranger == (Object)null)
					{
						stranger = componentInParent;
					}
				}
			}
			float value = ThicketConfig.DigAssist.Value;
			if (value <= 0f)
			{
				return null;
			}
			Pickable result = null;
			float num3 = Mathf.Cos(Mathf.Clamp(value, 0f, 89f) * ((float)Math.PI / 180f));
			Collider[] array3 = Physics.OverlapSphere(((Component)player).transform.position, num);
			foreach (Collider val2 in array3)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Pickable componentInParent2 = ((Component)val2).GetComponentInParent<Pickable>();
				if ((Object)(object)componentInParent2 == (Object)null || Of(componentInParent2) == null)
				{
					continue;
				}
				Vector3 val3 = ((Component)componentInParent2).transform.position + Vector3.up * 0.3f - position;
				if (!(((Vector3)(ref val3)).sqrMagnitude < 0.0001f))
				{
					float num4 = Vector3.Dot(((Vector3)(ref val3)).normalized, forward);
					if (!(num4 <= num3))
					{
						num3 = num4;
						result = componentInParent2;
					}
				}
			}
			return result;
		}

		private static string Humanise(Pickable pickable)
		{
			string prefabName = Utils.GetPrefabName(((Object)((Component)pickable).gameObject).name);
			if (!string.IsNullOrEmpty(pickable.m_overrideName))
			{
				return Localization.instance.Localize(pickable.m_overrideName);
			}
			if ((Object)(object)pickable.m_itemPrefab != (Object)null)
			{
				ItemDrop component = pickable.m_itemPrefab.GetComponent<ItemDrop>();
				if ((Object)(object)component != (Object)null && component.m_itemData != null && component.m_itemData.m_shared != null)
				{
					return Localization.instance.Localize(component.m_itemData.m_shared.m_name);
				}
			}
			return prefabName;
		}

		private static void Dig(Player player, Pickable pickable, WildPlant plant)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			if (plant.Level > 0 && ((Character)player).GetSkillFactor((SkillType)106) * 100f < (float)plant.Level)
			{
				((Character)player).Message((MessageType)2, plant.Title + " needs Farming " + plant.Level + " to dig up", 0, (Sprite)null, false);
				return;
			}
			ZNetView component = ((Component)pickable).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return;
			}
			component.ClaimOwnership();
			Vector3 position = ((Component)pickable).transform.position;
			bool flag = PickedRef.Invoke(pickable);
			if (!flag && (Object)(object)pickable.m_itemPrefab != (Object)null)
			{
				for (int i = 0; i < Mathf.Max(1, pickable.m_amount); i++)
				{
					Object.Instantiate<GameObject>(pickable.m_itemPrefab, position + Vector3.up * 0.3f + Random.insideUnitSphere * 0.2f, Quaternion.identity);
				}
			}
			ZNetScene.instance.Destroy(((Component)pickable).gameObject);
			Carry.Begin(player, plant, flag);
			((Character)player).RaiseSkill((SkillType)106, 1f);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Plant), "Grow")]
		private static void Grown(Plant __instance, GameObject __result)
		{
			Pickable val = default(Pickable);
			if (!((Object)(object)__result == (Object)null) && WildPlants.OfPieceName(Utils.GetPrefabName(((Object)((Component)__instance).gameObject).name)) != null && __result.TryGetComponent<Pickable>(ref val))
			{
				val.SetPicked(true);
			}
		}
	}
	internal sealed class WildPlant
	{
		public enum Shape
		{
			Bush,
			Mushroom,
			Thistle,
			Dandelion
		}

		public string Id;

		public string PieceName;

		public string Title;

		public string ItemName;

		public string Grown;

		public Shape Form;

		public string Model;

		public string Icon;

		public bool TolerateCold;

		public string DefaultRow;

		public string Note;

		public int Level;

		public string CostItem;

		public int CostAmount;

		public Biome Biomes;

		public float GrowMin;

		public float GrowMax;

		public float Radius => Form switch
		{
			Shape.Bush => 1.6f, 
			Shape.Thistle => 0.5f, 
			Shape.Dandelion => 0.4f, 
			_ => 0.5f, 
		};

		public bool Read(string row)
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			string[] array = (row ?? "").Split(new char[1] { '|' });
			if (array.Length < 4)
			{
				if (!string.IsNullOrEmpty(row))
				{
					WildPlants.Warn(Id + ": a row needs four fields separated by | - using the default instead.");
				}
				array = DefaultRow.Split(new char[1] { '|' });
			}
			Level = Mathf.Max(0, Int(array[0], 0));
			string[] array2 = array[1].Split(new char[1] { ':' });
			CostItem = array2[0].Trim();
			CostAmount = ((array2.Length <= 1) ? 1 : Mathf.Max(1, Int(array2[1], 1)));
			Biomes = ParseBiomes(array[2]);
			if ((int)Biomes == 0)
			{
				WildPlants.Warn(Id + ": no biome in " + array[2].Trim() + " was recognised. It would never grow anywhere, so the default is used.");
				Biomes = ParseBiomes(DefaultRow.Split(new char[1] { '|' })[2]);
			}
			ParseRange(array[3]);
			return CostItem.Length > 0;
		}

		private static int Int(string text, int fallback)
		{
			if (!int.TryParse((text ?? "").Trim(), out var result))
			{
				return fallback;
			}
			return result;
		}

		private static Biome ParseBiomes(string text)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			Biome val = (Biome)0;
			string[] array = (text ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length != 0)
				{
					try
					{
						val = (Biome)(val | (Biome)Enum.Parse(typeof(Biome), text2, ignoreCase: true));
					}
					catch (Exception)
					{
						WildPlants.Warn(text2 + " is not one of Valheim's biome names.");
					}
				}
			}
			return val;
		}

		private void ParseRange(string text)
		{
			string[] array = (text ?? "").Trim().Split(new char[1] { '-' });
			GrowMin = Mathf.Max(1f, (float)Int(array[0], 1800));
			GrowMax = ((array.Length > 1) ? Mathf.Max(GrowMin, (float)Int(array[1], (int)GrowMin)) : GrowMin);
		}

		public static List<WildPlant> Roster()
		{
			return new List<WildPlant>
			{
				new WildPlant
				{
					Id = "Dandelion",
					PieceName = "thicket_dandelion",
					ItemName = "thicket_uprooted_dandelion",
					Title = "Dandelion",
					Grown = "Pickable_Dandelion",
					Form = Shape.Dandelion,
					Model = "thicket_dandelion.obj",
					Icon = "thicket_dandelion.png",
					DefaultRow = "5 | thicket_uprooted_dandelion:1 | Meadows | 60-120",
					Note = "The first one, and cheap. A meadow flower at Farming 5 is the rung that tells you the ladder is there at all."
				},
				new WildPlant
				{
					Id = "Raspberry",
					PieceName = "thicket_raspberry",
					ItemName = "thicket_uprooted_raspberry",
					Title = "Raspberry bush",
					Grown = "RaspberryBush",
					Form = Shape.Bush,
					Model = "thicket_bush.obj",
					Icon = "thicket_raspberry.png",
					DefaultRow = "10 | thicket_uprooted_raspberry:1 | Meadows,BlackForest | 240-420",
					Note = "The one everybody wants first, so it is early and it is cheap."
				},
				new WildPlant
				{
					Id = "Mushroom",
					PieceName = "thicket_mushroom",
					ItemName = "thicket_uprooted_mushroom",
					Title = "Mushrooms",
					Grown = "Pickable_Mushroom",
					Form = Shape.Mushroom,
					Model = "thicket_mushroom.obj",
					Icon = "thicket_mushroom.png",
					DefaultRow = "15 | thicket_uprooted_mushroom:1 | Meadows,BlackForest | 90-180",
					Note = "Fast, because mushrooms are eaten about as fast as they are found."
				},
				new WildPlant
				{
					Id = "Blueberry",
					PieceName = "thicket_blueberry",
					ItemName = "thicket_uprooted_blueberry",
					Title = "Blueberry bush",
					Grown = "BlueberryBush",
					Form = Shape.Bush,
					Model = "thicket_bush.obj",
					Icon = "thicket_blueberry.png",
					DefaultRow = "25 | thicket_uprooted_blueberry:1 | BlackForest | 240-420",
					Note = "Black Forest only. A blueberry hedge in the meadows would be the point at which the biomes stop meaning anything."
				},
				new WildPlant
				{
					Id = "Thistle",
					PieceName = "thicket_thistle",
					ItemName = "thicket_uprooted_thistle",
					Title = "Thistle",
					Grown = "Pickable_Thistle",
					Form = Shape.Thistle,
					Model = "thicket_thistle.obj",
					Icon = "thicket_thistle.png",
					DefaultRow = "35 | thicket_uprooted_thistle:1 | BlackForest,Swamp | 120-240",
					Note = "Mid-ladder, because thistle is the first of these that is a chore to gather rather than a pleasure."
				},
				new WildPlant
				{
					Id = "MushroomYellow",
					PieceName = "thicket_mushroom_yellow",
					ItemName = "thicket_uprooted_mushroom_yellow",
					Title = "Yellow mushrooms",
					Grown = "Pickable_Mushroom_yellow",
					Form = Shape.Mushroom,
					Model = "thicket_mushroom.obj",
					Icon = "thicket_mushroom_yellow.png",
					DefaultRow = "45 | thicket_uprooted_mushroom_yellow:1 | BlackForest | 90-180",
					Note = "Grown outdoors in the Black Forest, which is not where you find them - they are a burial chamber crop and a chamber has a roof. Plant refuses anything under one, so the only place these can be farmed is the forest above the crypt."
				},
				new WildPlant
				{
					Id = "Cloudberry",
					PieceName = "thicket_cloudberry",
					ItemName = "thicket_uprooted_cloudberry",
					Title = "Cloudberry bush",
					Grown = "CloudberryBush",
					Form = Shape.Bush,
					Model = "thicket_bush.obj",
					Icon = "thicket_cloudberry.png",
					DefaultRow = "60 | thicket_uprooted_cloudberry:1 | Plains | 240-420",
					Note = "Late, and Plains only. Cloudberries are most of what makes the Plains worth farming in, and a Meadows cloudberry patch would quietly delete that reason."
				},
				new WildPlant
				{
					Id = "MushroomBlue",
					PieceName = "thicket_mushroom_blue",
					ItemName = "thicket_uprooted_mushroom_blue",
					Title = "Blue mushrooms",
					Grown = "Pickable_Mushroom_blue",
					Form = Shape.Mushroom,
					Model = "thicket_mushroom.obj",
					Icon = "thicket_mushroom_blue.png",
					TolerateCold = true,
					DefaultRow = "75 | thicket_uprooted_mushroom_blue:1 | Mountain | 90-180",
					Note = "Last, and the only one that needs the cold tolerance: the mountain is refused by Plant outright otherwise, and a plant that can never be healthy can never grow."
				}
			};
		}
	}
	internal static class WildPlants
	{
		private static readonly List<WildPlant> Plants = new List<WildPlant>();

		private static readonly Dictionary<WildPlant, GameObject> Prefabs = new Dictionary<WildPlant, GameObject>();

		private static readonly Dictionary<string, WildPlant> ByPiece = new Dictionary<string, WildPlant>();

		private static readonly Dictionary<string, WildPlant> ByGrown = new Dictionary<string, WildPlant>();

		private static readonly HashSet<string> Said = new HashSet<string>();

		private static float _nextKnownNudge;

		private static FieldRef<Player, HashSet<string>> _knownRef;

		private static bool _knownBindTried;

		private static readonly HashSet<WildPlant> Refused = new HashSet<WildPlant>();

		private static FieldRef<Player, HashSet<string>> KnownRef()
		{
			if (_knownBindTried)
			{
				return _knownRef;
			}
			_knownBindTried = true;
			try
			{
				_knownRef = AccessTools.FieldRefAccess<Player, HashSet<string>>("m_knownRecipes");
			}
			catch (Exception ex)
			{
				Warn("could not read Player.m_knownRecipes, so the Transplant entry will not be nudged into the build menu - it may stay hidden until you next learn a recipe or station. " + ex.Message);
			}
			return _knownRef;
		}

		public static void Bind(ConfigFile config)
		{
			Plants.Clear();
			ByPiece.Clear();
			foreach (WildPlant item in WildPlant.Roster())
			{
				ThicketConfig.Row(config, item);
				Plants.Add(item);
				ByPiece[item.PieceName] = item;
				ByGrown[item.Grown] = item;
			}
		}

		public static WildPlant Of(Piece piece)
		{
			if ((Object)(object)piece == (Object)null)
			{
				return null;
			}
			string name = ((Object)((Component)piece).gameObject).name;
			if (ByPiece.TryGetValue(name, out var value))
			{
				return value;
			}
			int num = name.IndexOf("(Clone)", StringComparison.Ordinal);
			if (num <= 0)
			{
				return null;
			}
			if (!ByPiece.TryGetValue(name.Substring(0, num), out value))
			{
				return null;
			}
			return value;
		}

		public static WildPlant OfGrown(string prefabName)
		{
			if (!ByGrown.TryGetValue(prefabName, out var value))
			{
				return null;
			}
			return value;
		}

		public static WildPlant OfPieceName(string prefabName)
		{
			if (!ByPiece.TryGetValue(prefabName, out var value))
			{
				return null;
			}
			return value;
		}

		public static bool Register()
		{
			if (!ThicketConfig.Enabled.Value)
			{
				return true;
			}
			if ((Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return false;
			}
			if (ObjectDB.instance.m_items == null || ObjectDB.instance.m_items.Count == 0)
			{
				return false;
			}
			PieceTable val = CultivatorPieces();
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			GameObject val2 = WildPrefab.BuildTool();
			if ((Object)(object)val2 != (Object)null && !val.m_pieces.Contains(val2))
			{
				SitWithTheCrops(val2, val);
				val.m_pieces.Add(val2);
				GrovePlugin.Log.LogInfo((object)("Transplant added to the cultivator table (" + val.m_pieces.Count + " pieces)."));
			}
			if ((Object)(object)val2 != (Object)null && Time.time > _nextKnownNudge)
			{
				_nextKnownNudge = Time.time + 5f;
				Player localPlayer = Player.m_localPlayer;
				FieldRef<Player, HashSet<string>> val3 = KnownRef();
				if ((Object)(object)localPlayer != (Object)null && val3 != null)
				{
					HashSet<string> hashSet = val3.Invoke(localPlayer);
					if (hashSet != null && !hashSet.Contains("Transplant") && val.m_pieces.Contains(val2))
					{
						try
						{
							AccessTools.Method(typeof(Player), "UpdateKnownRecipesList", (Type[])null, (Type[])null).Invoke(localPlayer, null);
							GrovePlugin.Log.LogInfo((object)"Nudged the known-pieces update for Transplant.");
						}
						catch (Exception ex)
						{
							GrovePlugin.Log.LogWarning((object)("Could not nudge known pieces: " + ex.Message));
						}
					}
				}
			}
			bool result = true;
			foreach (WildPlant plant in Plants)
			{
				if (!Refused.Contains(plant) && !Register(plant, val))
				{
					result = false;
				}
			}
			return result;
		}

		private static void SitWithTheCrops(GameObject tool, PieceTable table)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			Piece val = default(Piece);
			if (!tool.TryGetComponent<Piece>(ref val))
			{
				return;
			}
			Piece val2 = default(Piece);
			foreach (GameObject piece in table.m_pieces)
			{
				if (!((Object)(object)piece == (Object)null) && !((Object)(object)piece == (Object)(object)tool) && piece.TryGetComponent<Piece>(ref val2) && !val2.m_repairPiece && !val2.m_removePiece)
				{
					val.m_category = val2.m_category;
					val.m_usage = val2.m_usage;
					break;
				}
			}
		}

		private static bool Register(WildPlant plant, PieceTable table)
		{
			ZNetScene instance = ZNetScene.instance;
			GameObject prefab = instance.GetPrefab(plant.PieceName);
			if (!Prefabs.TryGetValue(plant, out var value) || (Object)(object)value == (Object)null)
			{
				if (!plant.Read(ThicketConfig.RowFor(plant.Id)))
				{
					Refused.Add(plant);
					return true;
				}
				value = WildPrefab.Build(plant);
				if ((Object)(object)value == (Object)null)
				{
					Refused.Add(plant);
					return true;
				}
				Prefabs[plant] = value;
				ThicketConfig.Say(GrovePlugin.Log, plant.Id + " built.");
			}
			if ((Object)(object)prefab == (Object)null)
			{
				AddToScene(plant.PieceName, value);
			}
			return (Object)(object)instance.GetPrefab(plant.PieceName) != (Object)null;
		}

		private static void AddToScene(string name, GameObject prefab)
		{
			ZNetScene instance = ZNetScene.instance;
			if (!instance.m_prefabs.Contains(prefab))
			{
				instance.m_prefabs.Add(prefab);
			}
			try
			{
				((Dictionary<int, GameObject>)AccessTools.Field(typeof(ZNetScene), "m_namedPrefabs").GetValue(instance))[StringExtensionMethods.GetStableHashCode(name)] = prefab;
			}
			catch (Exception ex)
			{
				GrovePlugin.Log.LogError((object)("Could not register " + name + ": " + ex.Message));
			}
		}

		private static void AddToCultivator(WildPlant plant, GameObject prefab, PieceTable table)
		{
			table.m_pieces.Add(prefab);
			GrovePlugin.Log.LogInfo((object)(plant.Title + " added to the cultivator."));
		}

		private static PieceTable CultivatorPieces()
		{
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Cultivator");
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val == (Object)null || val.m_itemData == null || val.m_itemData.m_shared == null)
			{
				return null;
			}
			PieceTable buildPieces = val.m_itemData.m_shared.m_buildPieces;
			if (!((Object)(object)buildPieces != (Object)null) || buildPieces.m_pieces == null)
			{
				return null;
			}
			return buildPieces;
		}

		public static void Warn(string message)
		{
			if (GrovePlugin.Log != null && Said.Add(message))
			{
				GrovePlugin.Log.LogWarning((object)("Thicket: " + message));
			}
		}
	}
	internal static class WildPrefab
	{
		private static FieldRef<Plant, GameObject> _healthyRef;

		private static FieldRef<Plant, GameObject> _unhealthyRef;

		private static FieldRef<Plant, GameObject> _healthyGrownRef;

		private static FieldRef<Plant, GameObject> _unhealthyGrownRef;

		private static bool _stageBindTried;

		private static GameObject _holder;

		private static readonly Dictionary<string, ModelData> Meshes = new Dictionary<string, ModelData>();

		private static readonly HashSet<int> Remapped = new HashSet<int>();

		public const string ToolName = "thicket_transplant";

		private static GameObject _tool;

		private static bool BindStages()
		{
			if (!_stageBindTried)
			{
				_stageBindTried = true;
				try
				{
					_healthyRef = AccessTools.FieldRefAccess<Plant, GameObject>("m_healthy");
					_unhealthyRef = AccessTools.FieldRefAccess<Plant, GameObject>("m_unhealthy");
					_healthyGrownRef = AccessTools.FieldRefAccess<Plant, GameObject>("m_healthyGrown");
					_unhealthyGrownRef = AccessTools.FieldRefAccess<Plant, GameObject>("m_unhealthyGrown");
				}
				catch (Exception ex)
				{
					_healthyRef = null;
					_unhealthyRef = null;
					_healthyGrownRef = null;
					_unhealthyGrownRef = null;
					WildPlants.Warn("could not bind Plant's stage fields (m_healthy and its three siblings), so a transplanted seedling will not show the right mesh and Plant.SUpdate will throw against it about once every ten seconds. The seedlings ARE still registered, so nothing already planted is lost - which is why this is a complaint rather than a refusal to build them. " + ex.Message);
				}
			}
			if (_healthyRef != null && _unhealthyRef != null && _healthyGrownRef != null)
			{
				return _unhealthyGrownRef != null;
			}
			return false;
		}

		private static GameObject Holder()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			if ((Object)(object)_holder == (Object)null)
			{
				_holder = new GameObject("VaettirPrefabs");
				_holder.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)_holder);
			}
			return _holder;
		}

		public static GameObject Build(WildPlant plant)
		{
			//IL_00e7: 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_0111: Unknown result type (might be due to invalid IL or missing references)
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			GameObject val = Donor(instance);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			GameObject prefab = instance.GetPrefab(plant.Grown);
			if ((Object)(object)prefab == (Object)null)
			{
				WildPlants.Warn(plant.Id + ": there is no prefab called " + plant.Grown + " in this world, so it cannot be planted.");
				return null;
			}
			if ((Object)(object)prefab.GetComponent<ZNetView>() == (Object)null)
			{
				WildPlants.Warn(plant.Id + ": " + plant.Grown + " has no ZNetView, so growing one would throw inside Plant.Grow. Skipped.");
				return null;
			}
			ModelData modelData = Mesh(plant);
			if (modelData == null)
			{
				return null;
			}
			Holder();
			bool forceDisableInit = ZNetView.m_forceDisableInit;
			ZNetView.m_forceDisableInit = true;
			GameObject val2;
			try
			{
				val2 = Object.Instantiate<GameObject>(val, _holder.transform);
			}
			finally
			{
				ZNetView.m_forceDisableInit = forceDisableInit;
			}
			((Object)val2).name = plant.PieceName;
			val2.transform.localRotation = Quaternion.identity;
			val2.transform.localScale = Vector3.one * Mathf.Max(0.1f, ThicketConfig.Scale.Value);
			Strip(val2);
			GameObject[] stages = Visuals(val2, modelData);
			Sow(val2, plant, prefab, stages);
			Dress(val2, plant);
			GrovePlugin.Log.LogInfo((object)("Thicket built " + plant.PieceName + " from " + ((Object)val).name + ", growing into " + plant.Grown + "."));
			return val2;
		}

		private static GameObject Donor(ZNetScene scene)
		{
			string[] array = new string[2]
			{
				ThicketConfig.Donor.Value,
				"sapling_carrot"
			};
			foreach (string text in array)
			{
				if (!string.IsNullOrEmpty(text))
				{
					GameObject prefab = scene.GetPrefab(text);
					if ((Object)(object)prefab != (Object)null)
					{
						return prefab;
					}
					GrovePlugin.LogOnce("Thicket donor " + text + " does not exist.");
				}
			}
			return null;
		}

		private static ModelData Mesh(WildPlant plant)
		{
			if (Meshes.TryGetValue(plant.Model, out var value))
			{
				return value;
			}
			ModelData modelData = ObjMesh.Load(Path.Combine(Path.GetDirectoryName(typeof(WildPrefab).Assembly.Location), plant.Model));
			if (modelData == null)
			{
				GrovePlugin.LogOnce("Thicket model " + plant.Model + " is missing from beside the dll, so " + plant.Id + " cannot be built.");
			}
			Meshes[plant.Model] = modelData;
			return modelData;
		}

		private static void Strip(GameObject clone)
		{
			Pickable[] componentsInChildren = clone.GetComponentsInChildren<Pickable>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			MeshRenderer[] componentsInChildren2 = clone.GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val in componentsInChildren2)
			{
				if (!((Object)(object)val == (Object)null))
				{
					Object.DestroyImmediate((Object)(object)((Component)val).gameObject);
				}
			}
		}

		private static GameObject[] Visuals(GameObject clone, ModelData model)
		{
			GameObject val = Stage(clone, model, "healthy", 0.6f, 0f);
			GameObject val2 = Stage(clone, model, "unhealthy", 0.6f, 22f);
			GameObject val3 = Stage(clone, model, "healthy_grown", 1f, 0f);
			GameObject val4 = Stage(clone, model, "unhealthy_grown", 1f, 22f);
			return (GameObject[])(object)new GameObject[4] { val, val2, val3, val4 };
		}

		private static GameObject Stage(GameObject clone, ModelData model, string name, float scale, float lean)
		{
			//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_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			GameObject val = new GameObject(name);
			val.transform.SetParent(clone.transform, false);
			val.transform.localScale = Vector3.one * scale;
			val.transform.localRotation = Quaternion.Euler(lean, 0f, 0f);
			val.AddComponent<MeshFilter>().sharedMesh = model.Mesh;
			((Renderer)val.AddComponent<MeshRenderer>()).sharedMaterials = Skins.Skin(model.Groups);
			if (Remapped.Add(((Object)model.Mesh).GetInstanceID()))
			{
				Skins.Remap(model.Mesh, model.Groups);
			}
			val.SetActive(false);
			return val;
		}

		private static void Sow(GameObject clone, WildPlant plant, GameObject grown, GameObject[] stages)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			Plant val = clone.GetComponent<Plant>();
			if ((Object)(object)val == (Object)null)
			{
				val = clone.AddComponent<Plant>();
			}
			val.m_name = plant.Title;
			val.m_grownPrefabs = (GameObject[])(object)new GameObject[1] { grown };
			val.m_growTime = plant.GrowMin;
			val.m_growTimeMax = plant.GrowMax;
			val.m_minScale = 1f;
			val.m_maxScale = 1f;
			val.m_growRadius = Mathf.Max(0.1f, plant.Radius * Mathf.Max(0.1f, ThicketConfig.SpacingScale.Value));
			val.m_biome = plant.Biomes;
			val.m_tolerateCold = plant.TolerateCold;
			val.m_needCultivatedGround = false;
			val.m_destroyIfCantGrow = false;
			val.m_attachDistance = 0f;
			if (BindStages())
			{
				_healthyRef.Invoke(val) = stages[0];
				_unhealthyRef.Invoke(val) = stages[1];
				_healthyGrownRef.Invoke(val) = stages[2];
				_unhealthyGrownRef.Invoke(val) = stages[3];
			}
		}

		private static void Dress(GameObject clone, WildPlant plant)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			Piece component = clone.GetComponent<Piece>();
			if (!((Object)(object)component == (Object)null))
			{
				component.m_name = plant.Title;
				component.m_description = Describe(plant);
				component.m_resources = (Requirement[])(object)new Requirement[0];
				Sprite val = Icons.Load(plant.Icon, plant.PieceName);
				if ((Object)(object)val != (Object)null)
				{
					component.m_icon = val;
				}
				component.m_groundPiece = true;
				component.m_groundOnly = true;
				component.m_cultivatedGroundOnly = false;
				component.m_onlyInBiome = plant.Biomes;
				component.m_targetNonPlayerBuilt = false;
			}
		}

		private static string Describe(WildPlant plant)
		{
			string text = "Replant an uprooted one where it grows wild, and it will take root again in its own time. Dig one up with the cultivator.";
			if (!ThicketConfig.SayTheLevel.Value || plant.Level <= 0)
			{
				return text;
			}
			return text + "\nNeeds Farming " + plant.Level + ".";
		}

		private static Requirement[] Requirements(WildPlant plant)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(plant.CostItem);
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val == (Object)null)
			{
				WildPlants.Warn(plant.Id + ": there is no item called " + plant.CostItem + ", so it would cost nothing at all. Left free - fix the row.");
				return (Requirement[])(object)new Requirement[0];
			}
			return (Requirement[])(object)new Requirement[1]
			{
				new Requirement
				{
					m_resItem = val,
					m_amount = plant.CostAmount,
					m_recover = false
				}
			};
		}

		public static GameObject BuildTool()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			if ((Object)(object)_tool != (Object)null)
			{
				return _tool;
			}
			_tool = new GameObject("thicket_transplant");
			_tool.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)_tool);
			Piece val = _tool.AddComponent<Piece>();
			val.m_name = "Transplant";
			val.m_description = "Click a wild plant to dig it up, roots and all. You can only walk while you carry it; click open ground to plant it back down. Each plant asks its own Farming level.";
			val.m_repairPiece = false;
			val.m_resources = (Requirement[])(object)new Requirement[0];
			val.m_groundPiece = false;
			Sprite val2 = Icons.Load("thicket_transplant.png", "thicket_transplant");
			if ((Object)(object)val2 == (Object)null)
			{
				GameObject val3 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab("RaspberryBush") : null);
				if ((Object)(object)val3 != (Object)null)
				{
					IconRender.NextShotBoost = 2.6f;
					val2 = IconRender.Shoot(val3, "thicket_transplant");
				}
			}
			if ((Object)(object)val2 != (Object)null)
			{
				val.m_icon = val2;
			}
			return _tool;
		}

		public static GameObject CarryVisual(WildPlant plant, bool picked)
		{
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(plant.Grown) : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			bool forceDisableInit = ZNetView.m_forceDisableInit;
			ZNetView.m_forceDisableInit = true;
			GameObject val2;
			try
			{
				val2 = Object.Instantiate<GameObject>(val);
			}
			finally
			{
				ZNetView.m_forceDisableInit = forceDisableInit;
			}
			((Object)val2).name = "thicket_carried_" + plant.Id;
			Pickable val3 = default(Pickable);
			if (picked && val2.TryGetComponent<Pickable>(ref val3) && (Object)(object)val3.m_hideWhenPicked != (Object)null)
			{
				val3.m_hideWhenPicked.SetActive(false);
			}
			MonoBehaviour[] componentsInChildren = val2.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val4 in componentsInChildren)
			{
				if ((Object)(object)val4 != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val4);
				}
			}
			ZNetView[] componentsInChildren2 = val2.GetComponentsInChildren<ZNetView>(true);
			foreach (ZNetView val5 in componentsInChildren2)
			{
				if ((Object)(object)val5 != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val5);
				}
			}
			Collider[] componentsInChildren3 = val2.GetComponentsInChildren<Collider>(true);
			foreach (Collider val6 in componentsInChildren3)
			{
				if ((Object)(object)val6 != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val6);
				}
			}
			Rigidbody[] componentsInChildren4 = val2.GetComponentsInChildren<Rigidbody>(true);
			foreach (Rigidbody val7 in componentsInChildren4)
			{
				if ((Object)(object)val7 != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val7);
				}
			}
			val2.transform.localScale = Vector3.one * ((plant.Form == WildPlant.Shape.Bush) ? 0.55f : 0.8f);
			val2.SetActive(true);
			return val2;
		}

		public static void WakeStage(GameObject planted)
		{
			Plant val = default(Plant);
			if (!((Object)(object)planted == (Object)null) && planted.TryGetComponent<Plant>(ref val) && BindStages())
			{
				GameObject val2 = _healthyRef.Invoke(val);
				if ((Object)(object)val2 != (Object)null)
				{
					val2.SetActive(true);
				}
			}
		}

		public static GameObject BuildItem(WildPlant plant)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab = ZNetScene.instance.GetPrefab("Wood");
			if ((Object)(object)prefab == (Object)null)
			{
				WildPlants.Warn(plant.Id + ": no Wood prefab to clone the item from.");
				return null;
			}
			ModelData modelData = Mesh(plant);
			if (modelData == null)
			{
				return null;
			}
			bool forceDisableInit = ZNetView.m_forceDisableInit;
			ZNetView.m_forceDisableInit = true;
			GameObject val;
			try
			{
				val = Object.Instantiate<GameObject>(prefab, Holder().transform);
			}
			finally
			{
				ZNetView.m_forceDisableInit = forceDisableInit;
			}
			((Object)val).name = plant.ItemName;
			MeshRenderer[] componentsInChildren = val.GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val2 in componentsInChildren)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					MeshFilter component = ((Component)val2).GetComponent<MeshFilter>();
					if ((Object)(object)component != (Object)null)
					{
						Object.DestroyImmediate((Object)(object)component);
					}
					Object.DestroyImmediate((Object)(object)val2);
				}
			}
			GameObject val3 = new GameObject(plant.ItemName + "_visual");
			val3.transform.SetParent(val.transform, false);
			val3.AddComponent<MeshFilter>().sharedMesh = modelData.Mesh;
			((Renderer)val3.AddComponent<MeshRenderer>()).sharedMaterials = Skins.Skin(modelData.Groups);
			if (Remapped.Add(((Object)modelData.Mesh).GetInstanceID()))
			{
				Skins.Remap(modelData.Mesh, modelData.Groups);
			}
			ItemDrop component2 = val.GetComponent<ItemDrop>();
			if ((Object)(object)component2 == (Object)null || component2.m_itemData == null || component2.m_itemData.m_shared == null)
			{
				WildPlants.Warn(plant.Id + ": the Wood donor has no usable ItemDrop.");
				return null;
			}
			SharedData shared = component2.m_itemData.m_shared;
			shared.m_name = "Uprooted " + Lower(plant.Title);
			shared.m_description = "Roots and all. It will not keep forever in a pocket, but it asks nothing except to be put back in the ground where its kind grows. Replant it with the cultivator.";
			shared.m_itemType = (ItemType)1;
			bool flag = plant.Form == WildPlant.Shape.Bush;
			shared.m_maxStackSize = (flag ? 1 : 10);
			shared.m_weight = (flag ? 4f : 0.5f);
			shared.m_teleportable = true;
			shared.m_questItem = false;
			Sprite val4 = Icons.Load(plant.Icon, plant.ItemName);
			if ((Object)(object)val4 != (Object)null)
			{
				shared.m_icons = (Sprite[])(object)new Sprite[1] { val4 };
			}
			component2.m_itemData.m_stack = 1;
			component2.m_itemData.m_dropPrefab = val;
			return val;
		}

		private static string Lower(string title)
		{
			if (!string.IsNullOrEmpty(title))
			{
				return char.ToLowerInvariant(title[0]) + title.Substring(1);
			}
			return title;
		}
	}
}
namespace Stow
{
	internal static class CarriedItem
	{
		private const float CarrySize = 0.17f;

		private static readonly Dictionary<string, GameObject> Templates = new Dictionary<string, GameObject>();

		private static GameObject _holder;

		public static void Invalidate()
		{
			foreach (GameObject value in Templates.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			Templates.Clear();
		}

		public static GameObject For(ItemData item)
		{
			return For(ItemGroups.PrefabNameOf(item));
		}

		public static GameObject For(string prefabName)
		{
			GameObject val = Template(prefabName);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			GameObject obj = Object.Instantiate<GameObject>(val);
			obj.SetActive(true);
			return obj;
		}

		private static GameObject Template(string prefabName)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			if (string.IsNullOrEmpty(prefabName))
			{
				return null;
			}
			if (Templates.TryGetValue(prefabName, out var value))
			{
				return value;
			}
			Templates[prefabName] = null;
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName);
			if ((Object)(object)itemPrefab == (Object)null)
			{
				return null;
			}
			if ((Object)(object)_holder == (Object)null)
			{
				_holder = new GameObject("StowCarriedItems");
				_holder.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)_holder);
			}
			bool forceDisableInit = ZNetView.m_forceDisableInit;
			ZNetView.m_forceDisableInit = true;
			GameObject val;
			try
			{
				val = Object.Instantiate<GameObject>(itemPrefab, _holder.transform);
			}
			finally
			{
				ZNetView.m_forceDisableInit = forceDisableInit;
			}
			((Object)val).name = "carried_" + prefabName;
			Strip(val);
			GameObject val2 = Fit(val);
			if ((Object)(object)val2 == (Object)null)
			{
				Object.DestroyImmediate((Object)(object)val);
				return null;
			}
			Templates[prefabName] = val2;
			return val2;
		}

		private static void Strip(GameObject clone)
		{
			List<Component> list = new List<Component>();
			MonoBehaviour[] componentsInChildren = clone.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null)
				{
					list.Add((Component)(object)val);
				}
			}
			Collider[] componentsInChildren2 = clone.GetComponentsInChildren<Collider>(true);
			foreach (Collider val2 in componentsInChildren2)
			{
				if ((Object)(object)val2 != (Object)null)
				{
					list.Add((Component)(object)val2);
				}
			}
			Rigidbody[] componentsInChildren3 = clone.GetComponentsInChildren<Rigidbody>(true);
			foreach (Rigidbody val3 in componentsInChildren3)
			{
				if ((Object)(object)val3 != (Object)null)
				{
					list.Add((Component)(object)val3);
				}
			}
			ParticleSystem[] componentsInChildren4 = clone.GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem val4 in componentsInChildren4)
			{
				if ((Object)(object)val4 != (Object)null)
				{
					list.Add((Component)(object)val4);
				}
			}
			TrailRenderer[] componentsInChildren5 = clone.GetComponentsInChildren<TrailRenderer>(true);
			foreach (TrailRenderer val5 in componentsInChildren5)
			{
				if ((Object)(object)val5 != (Object)null)
				{
					list.Add((Component)(object)val5);
				}
			}
			Light[] componentsInChildren6 = clone.GetComponentsInChildren<Light>(true);
			foreach (Light val6 in componentsInChildren6)
			{
				if ((Object)(object)val6 != (Object)null)
				{
					list.Add((Component)(object)val6);
				}
			}
			AudioSource[] componentsInChildren7 = clone.GetComponentsInChildren<AudioSource>(true);
			foreach (AudioSource val7 in componentsInChildren7)
			{
				if ((Object)(object)val7 != (Object)null)
				{
					list.Add((Component)(object)val7);
				}
			}
			foreach (Component item in list)
			{
				if ((Object)(object)item != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)item);
				}
			}
			ParticleSystemRenderer[] componentsInChildren8 = clone.GetComponentsInChildren<ParticleSystemRenderer>(true);
			foreach (ParticleSystemRenderer val8 in componentsInChildren8)
			{
				if ((Object)(object)val8 != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val8);
				}
			}
			Renderer[] componentsInChildren9 = clone.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val9 in componentsInChildren9)
			{
				if (!((Object)(object)val9 == (Object)null))
				{
					val9.shadowCastingMode = (ShadowCastingMode)0;
					val9.receiveShadows = false;
				}
			}
		}

		private static GameObject Fit(GameObject clone)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL