Decompiled source of Small Hands Immersive Greydwarf Workers v1.20.1

SmallHands_GreydwarfLaborer.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("SmallHands_GreydwarfLaborer")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.20.0.0")]
[assembly: AssemblyInformationalVersion("1.20.0")]
[assembly: AssemblyProduct("SmallHands_GreydwarfLaborer")]
[assembly: AssemblyTitle("SmallHands_GreydwarfLaborer")]
[assembly: AssemblyVersion("1.20.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SmallHands.GreydwarfLaborer
{
	internal sealed class ForageExtra
	{
		public sealed class Sweep
		{
			public Vector3 Pos;

			public float Until;

			public string Item = "";

			public readonly HashSet<int> Before = new HashSet<int>();
		}

		public float ForageScan;

		public float WaitChatAt;

		public float EmptySince = -1f;

		public bool Dirty = true;

		public bool Announced;

		public string Why = "";

		public string LastSig = "";

		public float LastLog;

		public float NextRelease;

		public float SweepUntil;

		public bool PickPending;

		public GameObject LastPick;

		public float LastPickAt;

		public readonly Dictionary<int, float> PickCooldown = new Dictionary<int, float>();

		public readonly List<Sweep> Sweeps = new List<Sweep>();
	}
	internal static class Forage
	{
		internal const int KindForage = 5;

		internal static ConfigEntry<float> Radius;

		internal static ConfigEntry<float> PickSeconds;

		internal static ConfigEntry<float> GiveUpMinutes;

		internal static ConfigEntry<float> ChatChance;

		internal static ConfigEntry<int> DeliverEvery;

		internal static ConfigEntry<string> Biomes;

		internal static ConfigEntry<bool> SkipFarmland;

		internal static ConfigEntry<bool> AllPlants;

		internal static ConfigEntry<bool> AlsoNearChest;

		internal static ConfigEntry<string> IgnoredItems;

		internal const string IgnoredDefault = "Wood,Stone,Flint,BoneFragments,Coal";

		internal static bool InternalPick;

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

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

		private static string _allowedText = "";

		private static readonly Dictionary<long, string> RefusalCache = new Dictionary<long, string>();

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

		private static readonly string[] NotPlantWords = new string[9] { "remains", "bone", "crypt", "skull", "egg", "stand", "buried", "grave", "scrap" };

		private static MethodInfo _findBiome;

		private static MethodInfo _wgBiome;

		private static MethodInfo _findHeightmap;

		private static MethodInfo _findHeightmapOut;

		private static MethodInfo _isCultivated;

		private static bool _looked;

		private static bool _warnedBiome;

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

		internal static void BindConfig(ConfigFile config)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Expected O, but got Unknown
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Expected O, but got Unknown
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Expected O, but got Unknown
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Expected O, but got Unknown
			Radius = config.Bind<float>("7 - Foraging", "Forage radius (m)", 60f, new ConfigDescription("How far from the plant you pointed at (and, with 'Also forage around the chest', from the chest) the Greydwarf looks for things to pick.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 150f), Array.Empty<object>()));
			AlsoNearChest = config.Bind<bool>("7 - Foraging", "Also forage around the chest", true, "On: besides the area around the plant you pointed at, it also forages within the same radius of its chest, so it keeps going near your base when the first spot is picked clean. Off: only the area around the plant you pointed at.");
			AllPlants = config.Bind<bool>("7 - Foraging", "Forage all plants in the area", true, "On: it picks every kind of wild plant around the one you pointed at (berries, mushrooms, flowers...). Off: only the same kind as the one you pointed at.");
			IgnoredItems = config.Bind<string>("7 - Foraging", "Ignored items", "Wood,Stone,Flint,BoneFragments,Coal", "Comma-separated item prefab names a Greydwarf never forages: sticks (Wood), stones (Stone), flint (Flint), bones (BoneFragments) and coal by default. They are never picked up, and can't be assigned either. A plant prefab name (e.g. Pickable_Stone) works too. Remains, bones, skulls, eggs and similar non-plants are always left alone, whatever this says.");
			if (IgnoredItems.Value == "Wood,Stone,Flint")
			{
				IgnoredItems.Value = "Wood,Stone,Flint,BoneFragments,Coal";
			}
			Biomes = config.Bind<string>("7 - Foraging", "Allowed biomes", "Meadows,BlackForest", "Comma-separated biomes where a Greydwarf will forage: Meadows, BlackForest, Swamp, Mountain, Plains, Mistlands, AshLands, DeepNorth. Plants in other biomes are ignored and can't be assigned. Plants without their own dialogue use generic lines.");
			DeliverEvery = config.Bind<int>("7 - Foraging", "Deliver every (items)", 20, new ConfigDescription("How many items it gathers before walking them to the chest.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500), Array.Empty<object>()));
			PickSeconds = config.Bind<float>("7 - Foraging", "Seconds per pick", 1.2f, new ConfigDescription("Time between picking one plant and the next.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 10f), Array.Empty<object>()));
			SkipFarmland = config.Bind<bool>("7 - Foraging", "Skip farmland", true, "Ignore plants growing on tilled farmland, so a Greydwarf never harvests your crops (and crops can't be assigned).");
			GiveUpMinutes = config.Bind<float>("7 - Foraging", "Give up when nothing is left (minutes)", 0f, new ConfigDescription("When everything in the area has been picked it waits by the chest for the plants to grow back and carries on by itself. 0 = keep waiting for as long as it takes. Otherwise it stops working after this many minutes with nothing to pick.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>()));
			ChatChance = config.Bind<float>("7 - Foraging", "Chatter chance (%)", 12f, new ConfigDescription("Chance that it comments on a plant when it picks one up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			Biomes.SettingChanged += delegate
			{
				Rebuild();
			};
			SkipFarmland.SettingChanged += delegate
			{
				Rebuild();
			};
			IgnoredItems.SettingChanged += delegate
			{
				Rebuild();
			};
			Rebuild();
		}

		private static string Norm(string s)
		{
			return s.Replace(" ", "").Replace("_", "").Replace("-", "")
				.ToLowerInvariant();
		}

		private static void Rebuild()
		{
			Allowed.Clear();
			RefusalCache.Clear();
			List<string> list = new List<string>();
			string[] array = Biomes.Value.Split(',', ';');
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.Length != 0)
				{
					Allowed.Add(Norm(text2));
					list.Add(Nice(text2));
				}
			}
			_allowedText = ((list.Count > 0) ? string.Join(", ", list.ToArray()) : "no biomes");
			Ignored.Clear();
			string[] array2 = IgnoredItems.Value.Split(',', ';');
			foreach (string text3 in array2)
			{
				string text4 = text3.Trim();
				if (text4.Length > 0)
				{
					Ignored.Add(text4);
				}
			}
		}

		internal static bool LooksLikeNotAPlant(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			string text = name.ToLowerInvariant();
			string[] notPlantWords = NotPlantWords;
			foreach (string value in notPlantWords)
			{
				if (text.Contains(value))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool IsIgnoredItem(string name)
		{
			return !string.IsNullOrEmpty(name) && (Ignored.Contains(name) || LooksLikeNotAPlant(name));
		}

		internal static bool IsIgnored(Pickable pk)
		{
			return IsIgnoredItem(ItemOf(pk)) || IsIgnoredItem(PickableName(pk));
		}

		internal static string IgnoredReason(Pickable pk)
		{
			string text = ItemOf(pk);
			return "Greydwarfs leave " + ((text.Length > 0) ? ItemName(text) : PickableName(pk)) + " alone (not a plant)";
		}

		internal static bool Wants(Pickable pk, string assignedPlant)
		{
			if (IsIgnored(pk))
			{
				return false;
			}
			if (AllPlants.Value)
			{
				return ItemOf(pk).Length > 0;
			}
			return assignedPlant.Length > 0 && PickableName(pk) == assignedPlant;
		}

		private static string Nice(string s)
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < s.Length; i++)
			{
				if (i > 0 && char.IsUpper(s[i]) && char.IsLower(s[i - 1]))
				{
					stringBuilder.Append(' ');
				}
				stringBuilder.Append(s[i]);
			}
			return stringBuilder.ToString();
		}

		private static void Lookup()
		{
			if (_looked)
			{
				return;
			}
			_looked = true;
			try
			{
				_findBiome = AccessTools.Method(typeof(Heightmap), "FindBiome", new Type[1] { typeof(Vector3) }, (Type[])null);
			}
			catch
			{
			}
			try
			{
				_wgBiome = AccessTools.Method(typeof(WorldGenerator), "GetBiome", new Type[1] { typeof(Vector3) }, (Type[])null);
			}
			catch
			{
			}
			try
			{
				MethodInfo[] methods = typeof(Heightmap).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (!(methodInfo.Name != "FindHeightmap"))
					{
						ParameterInfo[] parameters = methodInfo.GetParameters();
						if (parameters.Length == 2 && parameters[0].ParameterType == typeof(Vector3) && parameters[1].IsOut)
						{
							_findHeightmapOut = methodInfo;
							break;
						}
						if (parameters.Length == 1 && parameters[0].ParameterType == typeof(Vector3))
						{
							_findHeightmap = methodInfo;
						}
					}
				}
			}
			catch
			{
			}
			try
			{
				_isCultivated = AccessTools.Method(typeof(Heightmap), "IsCultivated", new Type[1] { typeof(Vector3) }, (Type[])null);
			}
			catch
			{
			}
			if (((_findHeightmap == null && _findHeightmapOut == null) || _isCultivated == null) && SkipFarmland != null && SkipFarmland.Value)
			{
				GreydwarfLaborerPlugin.Log.LogWarning((object)"[Forage] Couldn't find the game's farmland check, so 'Skip farmland' can't protect your crops. Don't assign crops to a Greydwarf.");
			}
		}

		private static string BiomeAt(Vector3 pos)
		{
			//IL_002c: 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)
			Lookup();
			try
			{
				string text = "";
				if (_findBiome != null)
				{
					object obj = _findBiome.Invoke(null, new object[1] { pos });
					if (obj != null)
					{
						text = obj.ToString();
					}
				}
				if ((text.Length == 0 || text == "None") && _wgBiome != null && WorldGenerator.instance != null)
				{
					object obj2 = _wgBiome.Invoke(WorldGenerator.instance, new object[1] { pos });
					if (obj2 != null)
					{
						text = obj2.ToString();
					}
				}
				return (text == "None") ? "" : text;
			}
			catch
			{
				return "";
			}
		}

		private static bool IsCultivated(Vector3 pos)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			Lookup();
			try
			{
				if (_isCultivated == null)
				{
					return false;
				}
				object obj = null;
				if (_findHeightmapOut != null)
				{
					object[] array = new object[2] { pos, null };
					if ((bool)_findHeightmapOut.Invoke(null, array))
					{
						obj = array[1];
					}
				}
				else if (_findHeightmap != null)
				{
					obj = _findHeightmap.Invoke(null, new object[1] { pos });
				}
				return obj != null && (bool)_isCultivated.Invoke(obj, new object[1] { pos });
			}
			catch
			{
				return false;
			}
		}

		internal static string Refusal(Vector3 pos)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			int num = Mathf.RoundToInt(pos.x);
			int num2 = Mathf.RoundToInt(pos.z);
			long key = ((long)num << 32) | (uint)num2;
			if (RefusalCache.TryGetValue(key, out var value))
			{
				return value;
			}
			string text = "";
			string text2 = BiomeAt(pos);
			if (text2.Length == 0)
			{
				if (!_warnedBiome)
				{
					_warnedBiome = true;
					GreydwarfLaborerPlugin.Log.LogWarning((object)"[Forage] Couldn't work out the biome, so the biome setting isn't applied.");
				}
			}
			else if (!Allowed.Contains(Norm(text2)))
			{
				text = "Greydwarfs only forage in the " + _allowedText + " (this is the " + Nice(text2) + ")";
			}
			if (text.Length == 0 && SkipFarmland.Value && IsCultivated(pos))
			{
				text = "That is farmland, not wild forage";
			}
			if (RefusalCache.Count > 8000)
			{
				RefusalCache.Clear();
			}
			RefusalCache[key] = text;
			return text;
		}

		internal static string PickableName(Pickable pk)
		{
			return LaborerSetup.CleanName(((Component)pk).gameObject);
		}

		internal static string ItemOf(Pickable pk)
		{
			GameObject val = Rf.Get<GameObject>(pk, "m_itemPrefab");
			return ((Object)(object)val != (Object)null) ? ((Object)val).name : "";
		}

		internal static bool IsPicked(Pickable pk)
		{
			if (Rf.Get(pk, "m_picked", def: false))
			{
				return true;
			}
			ZNetView componentInParent = ((Component)pk).GetComponentInParent<ZNetView>();
			return (Object)(object)componentInParent != (Object)null && componentInParent.IsValid() && componentInParent.GetZDO().GetBool("picked", false);
		}

		internal static string ItemName(string item)
		{
			if (string.IsNullOrEmpty(item))
			{
				return "plants";
			}
			if (NameCache.TryGetValue(item, out var value))
			{
				return value;
			}
			string text = item;
			try
			{
				GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(item) : null);
				ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val2 != (Object)null)
				{
					text = Localization.instance.Localize(val2.m_itemData.m_shared.m_name);
				}
			}
			catch
			{
			}
			NameCache[item] = text;
			return text;
		}

		internal static string Prompt(Pickable pk)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (IsIgnored(pk))
			{
				return "\n<color=#888888>" + IgnoredReason(pk) + "</color>";
			}
			string text = Refusal(((Component)pk).transform.position);
			if (text.Length > 0)
			{
				return "\n<color=#888888>" + text + "</color>";
			}
			return Localization.instance.Localize("\n[<color=yellow><b>Alt + $KEY_Use</b></color>] Assign Greydwarf to forage");
		}

		private static void Add(string key, params string[] lines)
		{
			ForageLines[key] = lines;
		}

		private static void BuildLines()
		{
			Add("forage_start", "Me go gather!", "Time for forage.", "Me sniff out snacks.", "Me go pick things.", "Forest, show me your treasures.", "Me know some good spots. Maybe.", "No basket. Me use arms.", "Gather time. Me happy.");
			Add("forage_generic", "Me pick it. Me not know what it is.", "Strange plant. Into the box.", "Pick pick pick.", "Forest give, me take.", "Me gather. Me good at gather.", "Edible? Maybe. Box anyway.", "Little treasure from the ground.", "Me find things. Me keep things. For boss.", "Gather gather gather.", "This plant smell funny. Me pick anyway.", "Not from around here, this one.", "Plant from far away. Me still pick.");
			Add("forage_assign_generic", "Me find those. Me bring them.", "Me pick this kind. Easy.", "Me forage! Me love forage.", "Okay. Me look under every leaf.", "Me know what to look for.", "Gather time!", "More of these? Me find more.", "Me sniff them out.");
			Add("forage_assign_all", "Me pick all the plants!", "Berries, mushrooms, flowers. Me get everything.", "Whole forest is a snack bar. Me gather.", "Everything that grow, me pick.", "Me check every bush. Every one.", "Plants, plants, plants! Me love plants.", "Me look under every leaf. Every leaf.", "Gather everything pretty. Everything tasty.");
			Add("forage_deliver", "Snacks in box!", "Box get gathered things.", "Me bring forest treasure.", "All picked, all delivered. Good me.", "Box smell like forest now.", "Gather done. Box happy.", "Special delivery from the bushes!", "Me carry so many. Me good gatherer.", "Fresh from the forest!", "Boss, look what me found!");
			Add("forage_wait", "Nothing left. Me wait for them to grow.", "Bushes empty. Me patient.", "Plants grow slow. Me wait.", "Me stare at bush. Bush not grow faster.", "Come on, grow grow grow.", "Me hum while waiting.", "Maybe me stare harder. Maybe it help.", "Me count leaves while wait.");
			Add("forage_done", "Nothing more to gather. Me rest now.", "Forest is empty of them. Me stop.", "All gathered. Me finish.", "No more plants. Me stand here.");
			Add("forage_raspberry", "Red berry! Me eat one. Me eat two.", "Raspberry is soft. Me squish it. Oops.", "Little red thing. Big happy.", "Sticky fingers. Worth it.", "Bush have thorns. Me have bark. Me win.", "One for box, one for me. Me say one for box.", "Berry bush give, me take. Fair.", "Smell like summer. Me not know summer.");
			Add("forageassign_raspberry", "Berries! Me love berries.", "Red berries? Me find them all.", "Me know where all the bushes hide.");
			Add("forage_blueberries", "Blue berry. Me turn blue? Me hope.", "Blueberries! Purple fingers!", "Me not eat. Me only taste. Little taste.", "Black Forest berries. Dark and sweet.", "Tiny blue ball. Many tiny blue ball.", "Me stain bark blue. Pretty.", "Bush hiding in shadow. Me find it.", "Blue is best color. Me decide.");
			Add("forageassign_blueberries", "Blue berries! Me hunt them.", "Little blue ones. Me find them all.", "Black Forest berries. Me go get.");
			Add("forage_mushroom", "Mushroom! Little hat on stick.", "Mushroom grow where it is dark. Me find.", "Cap smell like dirt. Good dirt.", "Me pick gentle. Mushroom shy.", "Little hat, big flavor.", "Mushroom friend. Me take friend.", "No spots. Safe. Probably.", "Me wear moss hat. Mushroom wear mushroom hat.");
			Add("forageassign_mushroom", "Mushrooms! Me sniff them out.", "Little hats on sticks. Me find them.", "Me know where mushrooms hide.");
			Add("forage_mushroomyellow", "Yellow mushroom. Bright like sunbeam.", "Yellow cap! Me feel fancy.", "Smell like forest floor.", "Mushroom not glow. Still good.", "Yellow one. Cheerful one.", "Me find yellow, me smile.", "Black Forest mushroom. Dark place, bright hat.", "Yellow like honey. Not honey. Still nice.");
			Add("forageassign_mushroomyellow", "Yellow mushrooms! Me go.", "Bright hats in dark woods. Me find.", "Me hunt yellow ones.");
			Add("forage_dandelion", "Dandelion! Yellow puff.", "Me blow the fluff. Whoosh.", "Pretty yellow flower. Me not squish.", "Flower head, flower stem. Me take both.", "Meadow flower. Smell like sunshine.", "Boss like flowers? Me hope.", "Make wish. Me wish for more seeds.", "Weed or flower? Me not sure.");
			Add("forageassign_dandelion", "Flowers! Me pick pretty ones.", "Yellow flowers in the meadow. Me go.", "Dandelions. Me find them.");
			Add("forage_thistle", "Ow! Pointy flower.", "Thistle have teeth. Me bark thicker.", "Purple and mean. Me respect.", "Prickly. Like me cousin.", "Careful careful. Ow. Okay.", "Spiky flower. Boss want this?", "Black Forest flower fight back.", "Thistle tickle. Not good tickle.");
			Add("forageassign_thistle", "Thistles. Me wear thick bark.", "Pointy flowers. Me gather them.", "Me pick the prickly ones.");
			Add("forage_flint", "Sharp rock. Me careful.", "Flint! Boss make sharp things.", "Rock that bite. Good rock.", "Gray and pointy. Me like.", "Me pick flint. Flint not complain.", "Clink clink. Good rock sound.", "Me could throw this. Me not. Me deliver.", "Sharp stone, sharp me.");
			Add("forageassign_flint", "Flint! Me find sharp rocks.", "Pointy rocks. Me gather.", "Boss need flint. Me bring flint.");
			Add("forage_stone", "Rock! Me know rock. Rock is friend.", "Stone is heavy. Me strong.", "Boring rock. Good rock.", "Stone go in box. Box get heavy.", "Me pick rock. Rock not mind.", "Smooth stone. Nice.", "Under rock is bugs. Me not look.", "Me pick rocks. Me feel like troll.");
			Add("forageassign_stone", "Rocks! Me collect rocks.", "Me find stones. Easy.", "Many rocks. Me carry.");
			Add("forage_carrotseeds", "Tiny seed. Tiny tiny.", "Carrot seeds! Boss grow orange things.", "Seed look like dust. Me hold careful.", "Me not eat. Me deliver.", "Little seed, big carrot.", "Black Forest give seeds. Nice.", "Me hope boss plant these.", "Plant seed, grow carrot. Magic.");
			Add("forageassign_carrotseeds", "Carrot seeds. Tiny but important.", "Me find seeds. Boss grow food.", "Seeds! Me look very close.");
			Add("forage_wood", "Stick! Little wood.", "Branch on ground. Free wood.", "Me pick up tree leftovers.", "Stick. Me like stick.", "Wood without chopping. Best wood.", "Tree drop this. Me take.", "Small wood, small effort.", "Me carry stick. Me feel like boss.");
			Add("forageassign_wood", "Sticks! Free wood.", "Branches on ground. Me gather.", "Me collect all the sticks.");
		}

		private static void InjectLines()
		{
			BuildLines();
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(Talk), "Lines");
				Dictionary<string, string[]> dictionary = ((fieldInfo != null) ? (fieldInfo.GetValue(null) as Dictionary<string, string[]>) : null);
				if (dictionary == null)
				{
					GreydwarfLaborerPlugin.Log.LogWarning((object)"[Forage] Couldn't find the dialogue table, so foraging has no dialogue.");
					return;
				}
				foreach (KeyValuePair<string, string[]> forageLine in ForageLines)
				{
					dictionary[forageLine.Key] = forageLine.Value;
				}
			}
			catch (Exception ex)
			{
				GreydwarfLaborerPlugin.Log.LogWarning((object)("[Forage] Adding dialogue failed: " + ex.Message));
			}
		}

		private static string PickKey(string item)
		{
			string text = "forage_" + item;
			return (!string.IsNullOrEmpty(item) && ForageLines.ContainsKey(text)) ? text : "forage_generic";
		}

		internal static void SayAssign(Character c, string item)
		{
			string text = "forageassign_" + item;
			if (string.IsNullOrEmpty(item) || !ForageLines.ContainsKey(text))
			{
				text = "forage_assign_generic";
			}
			if (AllPlants.Value && Random.value < 0.5f)
			{
				text = "forage_assign_all";
			}
			Talk.Say(c, text);
		}

		internal static bool RemapSay(Character c, ref string ctx, ref bool routine, ref float chance, ref bool result)
		{
			if ((Object)(object)c == (Object)null || ctx == null || !Tasks.IsForaging(c))
			{
				return true;
			}
			switch (ctx)
			{
			case "start":
				ctx = "forage_start";
				return true;
			case "deliver":
				ctx = "forage_deliver";
				return true;
			case "done":
				ctx = "forage_done";
				return true;
			case "chop":
			case "log":
			case "stump":
				result = false;
				return false;
			case "pickup":
				ctx = PickKey(Tasks.CurrentTargetName(c));
				routine = true;
				chance = ChatChance.Value;
				return true;
			default:
				return true;
			}
		}

		internal static void Apply(Harmony harmony)
		{
			InjectLines();
			Type[] array = new Type[9]
			{
				typeof(ForageHoverPatch),
				typeof(ForageInteractPatch),
				typeof(ForageTickPatch),
				typeof(ForageChestPatch),
				typeof(ForageTreePatch),
				typeof(ForageDebrisPatch),
				typeof(ForageUnassignPatch),
				typeof(ForageStatusPatch),
				typeof(ForageSayPatch)
			};
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				try
				{
					harmony.CreateClassProcessor(type).Patch();
					GreydwarfLaborerPlugin.Log.LogInfo((object)("Applied patch " + type.Name));
				}
				catch (Exception ex)
				{
					GreydwarfLaborerPlugin.Log.LogError((object)("Failed to apply patch " + type.Name + ": " + ex.Message));
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class ForageHoverPatch
	{
		private static MethodBase Target()
		{
			try
			{
				return AccessTools.Method(typeof(Pickable), "GetHoverText", (Type[])null, (Type[])null);
			}
			catch
			{
				return null;
			}
		}

		private static bool Prepare()
		{
			return Target() != null;
		}

		private static MethodBase TargetMethod()
		{
			return Target();
		}

		private static void Postfix(Pickable __instance, ref string __result)
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || (Object)(object)Tasks.FindFollower(localPlayer) == (Object)null || Forage.IsPicked(__instance))
				{
					return;
				}
				string text = Forage.Prompt(__instance);
				if (text.Length != 0)
				{
					string text2 = __result;
					if (string.IsNullOrEmpty(text2))
					{
						text2 = Localization.instance.Localize(__instance.GetHoverName());
					}
					__result = text2 + text;
				}
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch]
	internal static class ForageInteractPatch
	{
		private static MethodBase Target()
		{
			try
			{
				return AccessTools.Method(typeof(Pickable), "Interact", (Type[])null, (Type[])null);
			}
			catch
			{
				return null;
			}
		}

		private static bool Prepare()
		{
			return Target() != null;
		}

		private static MethodBase TargetMethod()
		{
			return Target();
		}

		private static bool Prefix(Pickable __instance, Humanoid __0, bool __1, ref bool __result)
		{
			try
			{
				if (Forage.InternalPick || __1 || !TaskInput.AltHeld())
				{
					return true;
				}
				Player val = (Player)(object)((__0 is Player) ? __0 : null);
				if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
				{
					return true;
				}
				if ((Object)(object)Tasks.FindFollower(val) == (Object)null)
				{
					return true;
				}
				Tasks.AssignForage(val, __instance);
				__result = true;
				return false;
			}
			catch
			{
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Tasks), "Tick")]
	internal static class ForageTickPatch
	{
		[HarmonyPriority(0)]
		private static bool Prefix(MonsterAI ai, Character c, float dt)
		{
			try
			{
				return !Tasks.RunForage(ai, c, dt);
			}
			catch
			{
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Tasks), "AssignChest")]
	internal static class ForageChestPatch
	{
		[HarmonyPriority(800)]
		private static bool Prefix(Player p, Container chest)
		{
			try
			{
				return !Tasks.AssignChestForForage(p, chest);
			}
			catch
			{
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Tasks), "AssignTree")]
	internal static class ForageTreePatch
	{
		private static void Prefix(Player p, ref Character __state)
		{
			__state = Tasks.FollowerChar(p);
		}

		private static void Postfix(Character __state)
		{
			Tasks.DropForageIfTree(__state);
		}
	}
	[HarmonyPatch(typeof(Tasks), "AssignDebris")]
	internal static class ForageDebrisPatch
	{
		private static void Prefix(Player p, ref Character __state)
		{
			__state = Tasks.FollowerChar(p);
		}

		private static void Postfix(Character __state)
		{
			Tasks.DropForageIfTree(__state);
		}
	}
	[HarmonyPatch(typeof(Tasks), "UnassignAll")]
	internal static class ForageUnassignPatch
	{
		private static void Postfix(Character c)
		{
			Tasks.ClearForageOf(c);
		}
	}
	[HarmonyPatch]
	internal static class ForageStatusPatch
	{
		private static MethodBase Target()
		{
			try
			{
				return AccessTools.Method(typeof(Tasks), "StatusOf", (Type[])null, (Type[])null);
			}
			catch
			{
				return null;
			}
		}

		private static bool Prepare()
		{
			return Target() != null;
		}

		private static MethodBase TargetMethod()
		{
			return Target();
		}

		private static void Postfix(Character c, ZDO zdo, ref string __result)
		{
			try
			{
				Tasks.ForageStatusOverride(c, zdo, ref __result);
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(Talk), "Say")]
	internal static class ForageSayPatch
	{
		private static bool Prefix(Character c, ref string ctx, ref bool routine, ref float chance, ref bool __result)
		{
			try
			{
				return Forage.RemapSay(c, ref ctx, ref routine, ref chance, ref __result);
			}
			catch
			{
				return true;
			}
		}
	}
	internal static class Tasks
	{
		internal sealed class SiteData
		{
			public bool Tamed;

			public bool Working;

			public string Name = "";

			public Vector3 Pos;

			public bool HasChest;

			public bool HasTree;

			public bool HasForage;

			public Vector3 Chest;

			public Vector3 Tree;

			public Vector3 Forage;

			public List<Vector3> Kilns = new List<Vector3>();
		}

		private const string KHasForage = "SmallHands_HasForage";

		private const string KForageName = "SmallHands_ForageName";

		private const string KForageItem = "SmallHands_ForageItem";

		private const string KForagePos = "SmallHands_ForagePos";

		private const float ForageReach = 2f;

		private static readonly ConditionalWeakTable<WorkState, ForageExtra> ForageExt = new ConditionalWeakTable<WorkState, ForageExtra>();

		private static readonly Collider[] ItemBuf = (Collider[])(object)new Collider[256];

		private static MethodInfo _pickInteract;

		private static float _nextFaultLog;

		private const string KKilns = "SmallHands_Kilns";

		private const int JobNone = 0;

		private const int JobFetch = 1;

		private const int JobDeliver = 2;

		private const int JobCoal = 3;

		private const int JobStore = 4;

		private static readonly MethodInfo QueueSizeM = AccessTools.Method(typeof(Smelter), "GetQueueSize", (Type[])null, (Type[])null);

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

		private static readonly MethodInfo ProcessedM = AccessTools.Method(typeof(Smelter), "GetProcessedQueueSize", (Type[])null, (Type[])null);

		private static readonly MethodInfo AddOreM = AccessTools.Method(typeof(Smelter), "RPC_AddOre", (Type[])null, (Type[])null);

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

		private static readonly MethodInfo QueueOreItemM = AccessTools.Method(typeof(Smelter), "QueueOreItem", (Type[])null, (Type[])null);

		private static string _addStrategy;

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

		internal const string StatusKey = "SmallHands_Status";

		internal const string PlayerTreeKey = "SmallHands_PlayerTree";

		internal static ConfigEntry<bool> ProtectPlayerTrees;

		private const string KSmelters = "SmallHands_Smelters";

		private const int JobSmFetch = 5;

		private const int JobSmDeliver = 6;

		internal static ConfigEntry<string> SmelterFuelItems;

		internal static ConfigEntry<int> SmelterMinLoad;

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

		private static readonly MethodInfo GetFuelM = AccessTools.Method(typeof(Smelter), "GetFuel", (Type[])null, (Type[])null);

		private static readonly MethodInfo AddFuelRpcM = AccessTools.Method(typeof(Smelter), "RPC_AddFuel", (Type[])null, (Type[])null);

		private static readonly FieldInfo FuelItemF = AccessTools.Field(typeof(Smelter), "m_fuelItem");

		private static readonly FieldInfo MaxFuelF = AccessTools.Field(typeof(Smelter), "m_maxFuel");

		private static string _fuelStrategy;

		internal static ConfigEntry<float> HealPerSecond;

		internal static ConfigEntry<bool> NoFightWhileFollowing;

		internal static ConfigEntry<int> MaxTreeTier;

		internal static ConfigEntry<float> ChopDamage;

		internal static ConfigEntry<float> ChopInterval;

		internal static ConfigEntry<float> WorkRadius;

		internal static ConfigEntry<bool> KeepChopping;

		internal static ConfigEntry<int> CarryCapacity;

		internal static ConfigEntry<float> SwingHitDelay;

		internal static ConfigEntry<float> StuckTimeout;

		internal static ConfigEntry<float> ClearRadius;

		internal static ConfigEntry<string> ChopAnimation;

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

		private static bool _loggedAnim;

		internal static ConfigEntry<string> CollectItems;

		internal static ConfigEntry<bool> ImmuneToTrees;

		internal static ConfigEntry<bool> RemoveStumps;

		internal static ConfigEntry<bool> ReplantTrees;

		internal static ConfigEntry<bool> IgnoreSpace;

		internal static ConfigEntry<string> KilnItems;

		internal static ConfigEntry<int> KilnRefillAt;

		internal static ConfigEntry<bool> KilnTopUp;

		internal static ConfigEntry<string> KilnOutput;

		internal static ConfigEntry<KeyCode> UnassignKey;

		internal static ConfigEntry<string> SaplingMap;

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

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

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

		private const string KWorking = "SmallHands_Working";

		private const string KTreePos = "SmallHands_TreePos";

		private const string KChestPos = "SmallHands_ChestPos";

		private const string KCarry = "SmallHands_Carry";

		private const string KHasTree = "SmallHands_HasTree";

		private const string KHasChest = "SmallHands_HasChest";

		private const string KPending = "SmallHands_Pending";

		private const float ChopReach = 2.2f;

		private const float PickupReach = 1.3f;

		private const float ProgressStep = 0.5f;

		private static readonly Dictionary<Character, WorkState> States = new Dictionary<Character, WorkState>();

		private static readonly MethodInfo MoveToM = AccessTools.Method(typeof(BaseAI), "MoveTo", new Type[4]
		{
			typeof(float),
			typeof(Vector3),
			typeof(float),
			typeof(bool)
		}, (Type[])null);

		private static readonly MethodInfo StopMovingM = AccessTools.Method(typeof(BaseAI), "StopMoving", (Type[])null, (Type[])null);

		private static readonly MethodInfo LookAtM = AccessTools.Method(typeof(BaseAI), "LookAt", new Type[1] { typeof(Vector3) }, (Type[])null);

		private static readonly MethodInfo InUseM = AccessTools.Method(typeof(Container), "IsInUse", (Type[])null, (Type[])null);

		private static MonsterAI _cachedFollower;

		private static float _cacheUntil;

		private const float SettleSeconds = 3f;

		private const float GiveUpSeconds = 120f;

		internal const string PlantedKey = "SmallHands_Planted";

		private static ForageExtra FxOf(WorkState st)
		{
			return ForageExt.GetOrCreateValue(st);
		}

		internal static bool IsForaging(Character c)
		{
			ZDO zDO = GetZDO((Component)(object)c);
			return zDO != null && zDO.GetBool("SmallHands_HasForage", false);
		}

		internal static Character FollowerChar(Player p)
		{
			MonsterAI val = FindFollower(p);
			return ((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Character>() : null;
		}

		internal static string CurrentTargetName(Character c)
		{
			if ((Object)(object)c == (Object)null || !States.TryGetValue(c, out var value) || value == null || (Object)(object)value.Target == (Object)null)
			{
				return "";
			}
			return LaborerSetup.CleanName(value.Target);
		}

		private static void ClearForage(ZDO zdo)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			zdo.Set("SmallHands_HasForage", false);
			zdo.Set("SmallHands_ForageName", "");
			zdo.Set("SmallHands_ForageItem", "");
			zdo.Set("SmallHands_ForagePos", Vector3.zero);
		}

		internal static void ClearForageOf(Character c)
		{
			ZDO zDO = GetZDO((Component)(object)c);
			if (zDO != null)
			{
				ClearForage(zDO);
			}
		}

		internal static void DropForageIfTree(Character c)
		{
			ZDO zDO = GetZDO((Component)(object)c);
			if (zDO != null && zDO.GetBool("SmallHands_HasTree", false))
			{
				ClearForage(zDO);
			}
		}

		internal static void AssignForage(Player p, Pickable pk)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			MonsterAI val = FindFollower(p);
			if ((Object)(object)val == (Object)null)
			{
				Msg(p, "No tamed Greydwarf is following you");
				return;
			}
			if (Forage.IsIgnored(pk))
			{
				Msg(p, Forage.IgnoredReason(pk));
				return;
			}
			string text = Forage.Refusal(((Component)pk).transform.position);
			if (text.Length > 0)
			{
				Msg(p, text);
				return;
			}
			Character component = ((Component)val).GetComponent<Character>();
			ZNetView val2 = View((Component)(object)component);
			if (!((Object)(object)val2 == (Object)null) && val2.IsValid())
			{
				string text2 = Forage.ItemOf(pk);
				val2.ClaimOwnership();
				ZDO zDO = val2.GetZDO();
				zDO.Set("SmallHands_ForageName", Forage.PickableName(pk));
				zDO.Set("SmallHands_ForageItem", text2);
				zDO.Set("SmallHands_ForagePos", ((Component)pk).transform.position);
				zDO.Set("SmallHands_HasForage", true);
				zDO.Set("SmallHands_HasTree", false);
				zDO.Set("SmallHands_TreePos", Vector3.zero);
				zDO.Set("SmallHands_Pending", "");
				Forage.SayAssign(component, text2);
				if (!zDO.GetBool("SmallHands_HasChest", false))
				{
					Msg(p, Forage.AllPlants.Value ? "Greydwarf will forage the plants around here. Alt + E on a chest to store them." : ("Greydwarf will forage " + Forage.ItemName(text2) + ". Alt + E on a chest to store them."));
				}
				else
				{
					StartWork(p, val, component, zDO);
				}
			}
		}

		internal static bool AssignChestForForage(Player p, Container chest)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			MonsterAI val = FindFollower(p);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Character component = ((Component)val).GetComponent<Character>();
			ZDO zDO = GetZDO((Component)(object)component);
			if (zDO == null || !zDO.GetBool("SmallHands_HasForage", false))
			{
				return false;
			}
			ZNetView val2 = View((Component)(object)component);
			if ((Object)(object)val2 == (Object)null || !val2.IsValid())
			{
				return false;
			}
			val2.ClaimOwnership();
			zDO.Set("SmallHands_ChestPos", ((Component)chest).transform.position);
			zDO.Set("SmallHands_HasChest", true);
			StartWork(p, val, component, zDO);
			return true;
		}

		internal static bool RunForage(MonsterAI ai, Character c, float dt)
		{
			ZDO zDO = GetZDO((Component)(object)c);
			if (zDO == null || !zDO.GetBool("SmallHands_HasForage", false))
			{
				return false;
			}
			WorkState workState = null;
			try
			{
				workState = GetState(c, zDO);
				if (HasStations(zDO) && KilnTick(ai, c, zDO, workState, dt, hasTree: false))
				{
					return true;
				}
				if (workState.Depositing || CarryTotal(workState) >= Forage.DeliverEvery.Value)
				{
					workState.Depositing = true;
					Deposit(ai, c, zDO, workState, dt);
					if (!workState.Depositing)
					{
						ForageExtra forageExtra = FxOf(workState);
						forageExtra.Dirty = true;
						forageExtra.ForageScan = 0f;
						forageExtra.EmptySince = -1f;
					}
					return true;
				}
				ForageTick(ai, c, zDO, workState, dt);
			}
			catch (Exception e)
			{
				ForageFault(workState, e);
			}
			return true;
		}

		private static void ForageFault(WorkState st, Exception e)
		{
			if (Time.time >= _nextFaultLog)
			{
				_nextFaultLog = Time.time + 30f;
				GreydwarfLaborerPlugin.Log.LogError((object)("[Forage] Hiccup while foraging (it carries on): " + e));
			}
			if (st != null)
			{
				ForageExtra forageExtra = FxOf(st);
				st.Target = null;
				st.HitPending = false;
				forageExtra.PickPending = false;
				forageExtra.Dirty = true;
				forageExtra.ForageScan = 1f;
			}
		}

		private static bool Blocked(WorkState st, int id, float now)
		{
			if (st.Ignore.TryGetValue(id, out var value) && now < value)
			{
				return true;
			}
			if (FxOf(st).PickCooldown.TryGetValue(id, out value) && now < value)
			{
				return true;
			}
			return false;
		}

		private static void ForageTick(MonsterAI ai, Character c, ZDO zdo, WorkState st, float dt)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: 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_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			CheckLastPick(st);
			FxOf(st).ForageScan -= dt;
			if (FxOf(st).ForageScan <= 0f)
			{
				FxOf(st).ForageScan = ((!((Object)(object)st.Target == (Object)null)) ? 3f : ((FxOf(st).EmptySince >= 0f) ? 5f : 1.5f));
				ForageScan(c, zdo, st);
			}
			Vector3 position = ((Component)c).transform.position;
			if ((Object)(object)st.Target == (Object)null)
			{
				st.HitPending = false;
				if (Time.time < FxOf(st).SweepUntil)
				{
					Stop((BaseAI)(object)ai);
					return;
				}
				if (FxOf(st).Dirty)
				{
					FxOf(st).ForageScan = 0f;
					Stop((BaseAI)(object)ai);
					return;
				}
				if (CarryTotal(st) > 0)
				{
					st.Depositing = true;
					return;
				}
				if (FxOf(st).EmptySince < 0f)
				{
					FxOf(st).EmptySince = Time.time;
				}
				float value = Forage.GiveUpMinutes.Value;
				if (value > 0f && Time.time - FxOf(st).EmptySince > value * 60f)
				{
					Stop((BaseAI)(object)ai);
					Finish(c, zdo, "nothing left to forage");
					return;
				}
				bool flag = zdo.GetBool("SmallHands_HasChest", false);
				Vector3 vec = zdo.GetVec3("SmallHands_ChestPos", position);
				float num = Flat(position, vec);
				if (flag && num > 6f)
				{
					MoveTo((BaseAI)(object)ai, dt, vec, 3.5f, num > 14f);
				}
				else
				{
					Stop((BaseAI)(object)ai);
				}
				if (Time.time >= FxOf(st).WaitChatAt)
				{
					FxOf(st).WaitChatAt = Time.time + 40f;
					Talk.Say(c, "forage_wait", routine: false, force: false, 15f);
				}
				return;
			}
			FxOf(st).EmptySince = -1f;
			GameObject target = st.Target;
			int instanceID = ((Object)target).GetInstanceID();
			bool flag2 = st.Kind == 1;
			Vector3 val = ClosestPoint(target, position);
			float num2 = (flag2 ? 1.3f : 2f);
			float num3 = Flat(position, val);
			bool flag3 = st.Remote.Contains(instanceID);
			if (num3 > num2 && !flag3)
			{
				if (IsStuck(st, target, num3, dt))
				{
					ResetProgress(st);
					if (flag2)
					{
						FxOf(st).PickCooldown[instanceID] = Time.time + 20f;
						PickUp(zdo, st);
						FxOf(st).ForageScan = 0f;
						FxOf(st).Dirty = true;
						FxOf(st).SweepUntil = Mathf.Min(FxOf(st).SweepUntil, Time.time + 1.5f);
					}
					else if (num3 <= 10f)
					{
						st.Remote.Add(instanceID);
						GreydwarfLaborerPlugin.Log.LogInfo((object)("Greydwarf can't walk to " + LaborerSetup.CleanName(target) + ", picking it from here."));
					}
					else
					{
						st.Ignore[instanceID] = Time.time + 60f;
						st.Target = null;
					}
				}
				else
				{
					MoveTo((BaseAI)(object)ai, dt, val, num2 * 0.8f, num3 > 8f);
				}
				return;
			}
			ResetProgress(st);
			Stop((BaseAI)(object)ai);
			LookAt((BaseAI)(object)ai, target.transform.position);
			if (flag2)
			{
				FxOf(st).PickCooldown[instanceID] = Time.time + 20f;
				PickUp(zdo, st);
				FxOf(st).SweepUntil = Mathf.Min(FxOf(st).SweepUntil, Time.time + 1.5f);
				FxOf(st).ForageScan = 0f;
				FxOf(st).Dirty = true;
				return;
			}
			if (st.HitPending)
			{
				if (!(Time.time < st.HitAt))
				{
					st.HitPending = false;
					if ((Object)(object)st.HitTarget == (Object)(object)st.Target)
					{
						DoPick(c, st, target);
					}
				}
				return;
			}
			st.ActionTimer -= dt;
			if (!(st.ActionTimer > 0f))
			{
				st.ActionTimer = Forage.PickSeconds.Value;
				PlaySwing(c);
				st.HitPending = true;
				st.HitAt = Time.time + SwingHitDelay.Value;
				st.HitTarget = st.Target;
			}
		}

		private static void Bump(SortedDictionary<string, int> d, string key)
		{
			d.TryGetValue(key, out var value);
			d[key] = value + 1;
		}

		private static string Listing(SortedDictionary<string, int> d)
		{
			if (d.Count == 0)
			{
				return "none";
			}
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, int> item in d)
			{
				list.Add(item.Key + " x" + item.Value);
			}
			return string.Join(", ", list.ToArray());
		}

		private static bool AcceptDrop(ForageExtra fx, GameObject go, string name, float now)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			int instanceID = ((Object)go).GetInstanceID();
			Vector3 position = go.transform.position;
			foreach (ForageExtra.Sweep sweep in fx.Sweeps)
			{
				if (now >= sweep.Until || Flat(position, sweep.Pos) >= 4.5f || sweep.Before.Contains(instanceID) || (sweep.Item.Length > 0 && !string.Equals(name, sweep.Item, StringComparison.OrdinalIgnoreCase)))
				{
					continue;
				}
				return true;
			}
			return false;
		}

		private static void ForageScan(Character c, ZDO zdo, WorkState st)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_02af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0418: Unknown result type (might be due to invalid IL or missing references)
			//IL_041d: Unknown result type (might be due to invalid IL or missing references)
			//IL_041f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0421: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_042f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0431: Unknown result type (might be due to invalid IL or missing references)
			//IL_037b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0384: Unknown result type (might be due to invalid IL or missing references)
			ForageExtra forageExtra = FxOf(st);
			string text = zdo.GetString("SmallHands_ForageName", "");
			if (text.Length == 0 && !Forage.AllPlants.Value)
			{
				st.Target = null;
				forageExtra.Dirty = false;
				forageExtra.Why = "No plant chosen";
				return;
			}
			Vector3 vec = zdo.GetVec3("SmallHands_ForagePos", ((Component)c).transform.position);
			Vector3 vec2 = zdo.GetVec3("SmallHands_ChestPos", Vector3.zero);
			bool flag = Forage.AlsoNearChest.Value && zdo.GetBool("SmallHands_HasChest", false) && vec2 != Vector3.zero;
			Vector3 position = ((Component)c).transform.position;
			float now = Time.time;
			float value = Forage.Radius.Value;
			forageExtra.Sweeps.RemoveAll((ForageExtra.Sweep sw) => now >= sw.Until);
			GameObject val = null;
			int kind = 0;
			float num = float.MaxValue;
			HashSet<int> hashSet = new HashSet<int>();
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			int num6 = 0;
			int num7 = 0;
			SortedDictionary<string, int> d = new SortedDictionary<string, int>();
			SortedDictionary<string, int> d2 = new SortedDictionary<string, int>();
			Pickable[] array = Object.FindObjectsByType<Pickable>((FindObjectsSortMode)0);
			Pickable[] array2 = array;
			foreach (Pickable val2 in array2)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				GameObject gameObject = ((Component)val2).gameObject;
				Vector3 position2 = gameObject.transform.position;
				if (Flat(position2, vec) > value && (!flag || !(Flat(position2, vec2) <= value)))
				{
					continue;
				}
				num2++;
				int instanceID = ((Object)gameObject).GetInstanceID();
				if (!hashSet.Add(instanceID))
				{
					continue;
				}
				if (!Forage.Wants(val2, text))
				{
					Bump(d2, Forage.ItemName(Forage.ItemOf(val2)));
					continue;
				}
				if (Forage.IsPicked(val2))
				{
					num4++;
					continue;
				}
				if (Forage.Refusal(position2).Length > 0)
				{
					num5++;
					continue;
				}
				if (Blocked(st, instanceID, now))
				{
					num6++;
					continue;
				}
				num3++;
				Bump(d, Forage.ItemName(Forage.ItemOf(val2)));
				float num9 = 1000f + Vector3.Distance(position, position2);
				if (num9 < num)
				{
					num = num9;
					val = gameObject;
					kind = 5;
				}
			}
			foreach (ForageExtra.Sweep sweep in forageExtra.Sweeps)
			{
				int num10 = Physics.OverlapSphereNonAlloc(sweep.Pos, 4.5f, ItemBuf, -1, (QueryTriggerInteraction)2);
				for (int num11 = 0; num11 < num10; num11++)
				{
					Collider val3 = ItemBuf[num11];
					ItemBuf[num11] = null;
					if ((Object)(object)val3 == (Object)null)
					{
						continue;
					}
					ItemDrop componentInParent = ((Component)val3).GetComponentInParent<ItemDrop>();
					if ((Object)(object)componentInParent == (Object)null)
					{
						continue;
					}
					GameObject gameObject2 = ((Component)componentInParent).gameObject;
					int instanceID2 = ((Object)gameObject2).GetInstanceID();
					if (!hashSet.Add(instanceID2) || Blocked(st, instanceID2, now))
					{
						continue;
					}
					string name = LaborerSetup.CleanName(gameObject2);
					if (!Forage.IsIgnoredItem(name) && AcceptDrop(forageExtra, gameObject2, name, now))
					{
						float num12 = Vector3.Distance(position, gameObject2.transform.position);
						if (num12 < num)
						{
							num = num12;
							val = gameObject2;
							kind = 1;
						}
					}
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				Pickable[] array3 = array;
				foreach (Pickable val4 in array3)
				{
					if (!((Object)(object)val4 == (Object)null))
					{
						Vector3 position3 = ((Component)val4).transform.position;
						if (!(Flat(position3, vec) <= value) && (!flag || !(Flat(position3, vec2) <= value)) && Forage.Wants(val4, text) && !Forage.IsPicked(val4))
						{
							num7++;
						}
					}
				}
			}
			if ((Object)(object)val != (Object)(object)st.Target)
			{
				ResetProgress(st);
				st.ActionTimer = Mathf.Min(st.ActionTimer, 0.3f);
				st.HitPending = false;
			}
			st.Target = val;
			st.Kind = kind;
			forageExtra.Dirty = false;
			if ((Object)(object)val == (Object)null && num6 > 0 && now >= forageExtra.NextRelease)
			{
				forageExtra.NextRelease = now + 15f;
				st.Ignore.Clear();
				forageExtra.PickCooldown.Clear();
				forageExtra.Dirty = true;
				forageExtra.ForageScan = 0.2f;
				GreydwarfLaborerPlugin.Log.LogInfo((object)("[Forage] " + num6 + " plant(s) were being skipped after an earlier failure; trying them again."));
			}
			if ((Object)(object)val != (Object)null)
			{
				forageExtra.Why = "";
			}
			else if (num3 == 0 && num6 == 0 && num4 > 0 && num5 == 0 && num7 == 0)
			{
				forageExtra.Why = "Waiting for plants to regrow";
			}
			else
			{
				List<string> list = new List<string>();
				if (num4 > 0)
				{
					list.Add(num4 + " picked");
				}
				if (num5 > 0)
				{
					list.Add(num5 + " in other biomes");
				}
				if (num7 > 0)
				{
					list.Add(num7 + " outside the area");
				}
				if (num6 > 0)
				{
					list.Add(num6 + " unreachable");
				}
				forageExtra.Why = "Nothing to pick" + ((list.Count > 0) ? (" (" + string.Join(", ", list.ToArray()) + ")") : " (no plants in range)");
			}
			string text2 = (((Object)(object)val != (Object)null) ? "found" : forageExtra.Why);
			if (!forageExtra.Announced || ((Object)(object)val == (Object)null && text2 != forageExtra.LastSig && now - forageExtra.LastLog > 15f))
			{
				string text3 = ((!forageExtra.Announced) ? "Looking around" : "Nothing to pick right now");
				forageExtra.Announced = true;
				forageExtra.LastLog = now;
				GreydwarfLaborerPlugin.Log.LogInfo((object)("[Forage] " + text3 + ": " + num3 + " to pick [" + Listing(d) + "]; " + num4 + " already picked; " + num7 + " outside the area; " + num5 + " in other biomes or on farmland; " + num6 + " skipped as unreachable; left alone [" + Listing(d2) + "] (" + num2 + " pickables within " + value + " m of the spot you chose" + (flag ? " or of the chest" : "") + ")."));
			}
			forageExtra.LastSig = text2;
			if (forageExtra.PickCooldown.Count <= 256)
			{
				return;
			}
			List<int> list2 = new List<int>();
			foreach (KeyValuePair<int, float> item in forageExtra.PickCooldown)
			{
				if (now >= item.Value)
				{
					list2.Add(item.Key);
				}
			}
			foreach (int item2 in list2)
			{
				forageExtra.PickCooldown.Remove(item2);
			}
		}

		private static void DoPick(Character c, WorkState st, GameObject go)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			Pickable val = (((Object)(object)go != (Object)null) ? go.GetComponentInParent<Pickable>() : null);
			st.Target = null;
			if ((Object)(object)val == (Object)null || Forage.IsPicked(val))
			{
				return;
			}
			GameObject gameObject = ((Component)val).gameObject;
			ForageExtra forageExtra = FxOf(st);
			ForageExtra.Sweep sweep = new ForageExtra.Sweep
			{
				Pos = gameObject.transform.position,
				Until = Time.time + 8f,
				Item = Forage.ItemOf(val)
			};
			int num = Physics.OverlapSphereNonAlloc(sweep.Pos, 4.5f, ItemBuf, -1, (QueryTriggerInteraction)2);
			for (int i = 0; i < num; i++)
			{
				Collider val2 = ItemBuf[i];
				ItemBuf[i] = null;
				ItemDrop val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponentInParent<ItemDrop>() : null);
				if ((Object)(object)val3 != (Object)null)
				{
					sweep.Before.Add(((Object)((Component)val3).gameObject).GetInstanceID());
				}
			}
			forageExtra.Sweeps.Add(sweep);
			if (forageExtra.Sweeps.Count > 12)
			{
				forageExtra.Sweeps.RemoveAt(0);
			}
			forageExtra.Dirty = true;
			FxOf(st).SweepUntil = Time.time + 4f;
			FxOf(st).PickCooldown[((Object)gameObject).GetInstanceID()] = Time.time + 6f;
			FxOf(st).LastPick = gameObject;
			FxOf(st).LastPickAt = Time.time;
			FxOf(st).PickPending = true;
			if (!InteractPick(val, c))
			{
				FxOf(st).PickPending = false;
			}
			FxOf(st).ForageScan = 0.4f;
		}

		private static void CheckLastPick(WorkState st)
		{
			if (!FxOf(st).PickPending || Time.time - FxOf(st).LastPickAt < 3f)
			{
				return;
			}
			FxOf(st).PickPending = false;
			GameObject lastPick = FxOf(st).LastPick;
			FxOf(st).LastPick = null;
			if (!((Object)(object)lastPick == (Object)null))
			{
				Pickable componentInParent = lastPick.GetComponentInParent<Pickable>();
				if ((Object)(object)componentInParent != (Object)null && !Forage.IsPicked(componentInParent))
				{
					st.Ignore[((Object)lastPick).GetInstanceID()] = Time.time + 180f;
					GreydwarfLaborerPlugin.Log.LogInfo((object)("Greydwarf couldn't pick " + LaborerSetup.CleanName(lastPick) + ", leaving it alone for a few minutes."));
				}
			}
		}

		private static bool InteractPick(Pickable pk, Character c)
		{
			if (_pickInteract == null)
			{
				try
				{
					_pickInteract = AccessTools.Method(typeof(Pickable), "Interact", (Type[])null, (Type[])null);
				}
				catch
				{
				}
			}
			if (_pickInteract == null)
			{
				GreydwarfLaborerPlugin.Log.LogError((object)"[Forage] Pickable.Interact not found; plants can't be picked.");
				return false;
			}
			ParameterInfo[] parameters = _pickInteract.GetParameters();
			object[] array = new object[parameters.Length];
			for (int i = 0; i < parameters.Length; i++)
			{
				Type parameterType = parameters[i].ParameterType;
				if (typeof(Humanoid).IsAssignableFrom(parameterType))
				{
					array[i] = ((object)((c is Humanoid) ? c : null)) ?? ((object)Player.m_localPlayer);
				}
				else if (parameterType == typeof(bool))
				{
					array[i] = false;
				}
				else
				{
					array[i] = (parameterType.IsValueType ? Activator.CreateInstance(parameterType) : null);
				}
			}
			Forage.InternalPick = true;
			try
			{
				_pickInteract.Invoke(pk, array);
				return true;
			}
			catch (Exception ex)
			{
				Exception ex2 = ((ex is TargetInvocationException && ex.InnerException != null) ? ex.InnerException : ex);
				GreydwarfLaborerPlugin.Log.LogWarning((object)("[Forage] Picking a plant failed: " + ex2.Message));
				return false;
			}
			finally
			{
				Forage.InternalPick = false;
			}
		}

		private static string ForageStatus(ZDO zdo, WorkState st)
		{
			ForageExtra forageExtra = FxOf(st);
			if (st.Depositing)
			{
				return (st.WaitTimer > 0.5f) ? "Waiting at chest" : "Delivering forage";
			}
			if ((Object)(object)st.Target == (Object)null)
			{
				if (Time.time < forageExtra.SweepUntil)
				{
					return "Gathering";
				}
				if (forageExtra.Dirty || forageExtra.Why.Length == 0)
				{
					return "Looking for plants";
				}
				return forageExtra.Why;
			}
			if (st.Kind == 1)
			{
				return "Gathering " + Forage.ItemName(LaborerSetup.CleanName(st.Target));
			}
			Pickable componentInParent = st.Target.GetComponentInParent<Pickable>();
			return "Foraging " + Forage.ItemName(((Object)(object)componentInParent != (Object)null) ? Forage.ItemOf(componentInParent) : "");
		}

		internal static void ForageStatusOverride(Character c, ZDO zdo, ref string result)
		{
			if (zdo != null && zdo.GetBool("SmallHands_Working", false) && zdo.GetBool("SmallHands_HasForage", false) && !((Object)(object)Care.GetAttacker(c) != (Object)null) && States.TryGetValue(c, out var value) && value != null && value.KilnJob == 0)
			{
				result = ForageStatus(zdo, value);
			}
		}

		internal static SiteData ReadSite(ZDO zdo)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: 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_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: 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_0135: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			if (zdo == null)
			{
				return null;
			}
			SiteData siteData = new SiteData();
			siteData.Tamed = zdo.GetBool("tamed", false);
			siteData.Name = zdo.GetString("TamedName", "");
			siteData.Working = zdo.GetBool("SmallHands_Working", false);
			siteData.Pos = zdo.GetPosition();
			Vector3 vec = zdo.GetVec3("SmallHands_ChestPos", Vector3.zero);
			siteData.HasChest = zdo.GetBool("SmallHands_HasChest", false) || vec != Vector3.zero;
			if (siteData.HasChest)
			{
				siteData.Chest = ((vec != Vector3.zero) ? vec : siteData.Pos);
			}
			Vector3 vec2 = zdo.GetVec3("SmallHands_TreePos", Vector3.zero);
			siteData.HasTree = zdo.GetBool("SmallHands_HasTree", false) || vec2 != Vector3.zero;
			if (siteData.HasTree)
			{
				siteData.Tree = ((vec2 != Vector3.zero) ? vec2 : siteData.Pos);
			}
			Vector3 vec3 = zdo.GetVec3("SmallHands_ForagePos", Vector3.zero);
			siteData.HasForage = zdo.GetBool("SmallHands_HasForage", false) && vec3 != Vector3.zero;
			if (siteData.HasForage)
			{
				siteData.Forage = vec3;
			}
			siteData.Kilns = LoadKilns(zdo);
			siteData.Kilns.AddRange(LoadSmelters(zdo));
			return siteData;
		}

		private static HashSet<string> Accepts(Smelter k)
		{
			string key = LaborerSetup.CleanName(((Component)k).gameObject);
			if (KilnAccepts.TryGetValue(key, out var value))
			{
				return value;
			}
			value = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (AccessTools.Field(typeof(Smelter), "m_conversion")?.GetValue(k) is IList list)
			{
				foreach (object item in list)
				{
					if (item != null)
					{
						object? obj = AccessTools.Field(item.GetType(), "m_from")?.GetValue(item);
						ItemDrop val = (ItemDrop)((obj is ItemDrop) ? obj : null);
						if ((Object)(object)val != (Object)null)
						{
							value.Add(((Object)((Component)val).gameObject).name);
						}
					}
				}
			}
			KilnAccepts[key] = value;
			return value;
		}

		internal static bool IsWoodKiln(Smelter k)
		{
			return (Object)(object)k != (Object)null && Accepts(k).Contains("Wood");
		}

		private static HashSet<string> KilnWood(Smelter k)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			HashSet<string> hashSet2 = Accepts(k);
			string[] array = KilnItems.Value.Split(',');
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.Length > 0 && hashSet2.Contains(text2))
				{
					hashSet.Add(text2);
				}
			}
			return hashSet;
		}

		private static int KilnMax(Smelter k)
		{
			return Rf.Get(k, "m_maxOre", 25);
		}

		private static int KilnQueued(Smelter k)
		{
			int num = 0;
			if (QueueSizeM != null)
			{
				try
				{
					num = (int)QueueSizeM.Invoke(k, null);
				}
				catch
				{
				}
			}
			ZDO zDO = GetZDO((Component)(object)k);
			int num2 = ((zDO != null) ? zDO.GetInt("queued", 0) : 0);
			return Mathf.Max(num, num2);
		}

		private static int KilnProcessed(Smelter k)
		{
			if (ProcessedM != null)
			{
				try
				{
					return (int)ProcessedM.Invoke(k, null);
				}
				catch
				{
				}
			}
			ZDO zDO = GetZDO((Component)(object)k);
			return (zDO != null) ? zDO.GetInt("processed", 0) : 0;
		}

		private static bool KilnEmpty(Smelter k)
		{
			return KilnQueued(k) == 0;
		}

		private static int KilnSpace(Smelter k)
		{
			return Mathf.Max(0, KilnMax(k) - KilnQueued(k));
		}

		private static void KilnLog(string key, string msg)
		{
			if (!LogGate.TryGetValue(key, out var value) || !(Time.time < value))
			{
				LogGate[key] = Time.time + 30f;
				GreydwarfLaborerPlugin.Log.LogInfo((object)("[Kiln] " + msg));
			}
		}

		private static bool AddToKiln(Smelter k, string item)
		{
			ZNetView val = Rf.Get<ZNetView>(k, "m_nview") ?? View((Component)(object)k);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				KilnLog("add-noview", "The kiln has no valid network view.");
				return false;
			}
			try
			{
				if (!val.IsOwner())
				{
					val.ClaimOwnership();
				}
			}
			catch
			{
			}
			bool flag = false;
			try
			{
				flag = val.IsOwner();
			}
			catch
			{
			}
			if (flag)
			{
				List<string> list = new List<string> { "queue", "rpc", "zdo" };
				if (_addStrategy != null && list.Remove(_addStrategy))
				{
					list.Insert(0, _addStrategy);
				}
				foreach (string item2 in list)
				{
					int num = KilnQueued(k);
					try
					{
						if (item2 == "queue" && QueueOreItemM != null)
						{
							QueueOreItemM.Invoke(k, new object[1] { item });
							goto IL_024c;
						}
						if (item2 == "rpc" && AddOreM != null)
						{
							AddOreM.Invoke(k, MatchArgs(AddOreM, item));
							goto IL_024c;
						}
						if (item2 == "zdo")
						{
							ZDO zDO = val.GetZDO();
							int num2 = zDO.GetInt("queued", 0);
							zDO.Set("item" + num2, item);
							zDO.Set("queued", num2 + 1);
							goto IL_024c;
						}
					}
					catch (Exception ex)
					{
						Exception ex2 = ((ex is TargetInvocationException && ex.InnerException != null) ? ex.InnerException : ex);
						KilnLog("add-ex-" + item2, "Adding wood via '" + item2 + "' threw: " + ex2.GetType().Name + ": " + ex2.Message);
					}
					continue;
					IL_024c:
					if (KilnQueued(k) > num)
					{
						if (_addStrategy != item2)
						{
							_addStrategy = item2;
							GreydwarfLaborerPlugin.Log.LogInfo((object)("[Kiln] Wood goes into kilns using the '" + item2 + "' method."));
						}
						return true;
					}
					KilnLog("add-nochange-" + item2, "Adding wood via '" + item2 + "' ran but the kiln's queue didn't grow.");
				}
			}
			else
			{
				KilnLog("add-notowner", "Couldn't take ownership of the kiln, so only the network call is available.");
			}
			try
			{
				val.InvokeRPC("RPC_AddOre", new object[1] { item });
				KilnLog("add-net", "Sent the add-wood network call. If kilns never fill, check the 'Smelter' dump above in this log.");
				return true;
			}
			catch (Exception ex3)
			{
				KilnLog("add-net-ex", "Network call failed: " + ex3.Message);
				return false;
			}
		}

		private static object[] MatchArgs(MethodInfo m, string item)
		{
			ParameterInfo[] parameters = m.GetParameters();
			object[] array = new object[parameters.Length];
			for (int i = 0; i < parameters.Length; i++)
			{
				Type parameterType = parameters[i].ParameterType;
				if (parameterType == typeof(long))
				{
					array[i] = 0L;
				}
				else if (parameterType == typeof(string))
				{
					array[i] = item;
				}
				else if (parameterType == typeof(int))
				{
					array[i] = 1;
				}
				else if (parameterType == typeof(bool))
				{
					array[i] = false;
				}
				else
				{
					array[i] = (parameterType.IsValueType ? Activator.CreateInstance(parameterType) : null);
				}
			}
			return array;
		}

		private static void DumpSmelter(Smelter k)
		{
			string text = LaborerSetup.CleanName(((Component)k).gameObject);
			if (!Dumped.Add(text))
			{
				return;
			}
			List<string> list = new List<string>();
			FieldInfo[] fields = typeof(Smelter).GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				list.Add(fieldInfo.FieldType.Name + " " + fieldInfo.Name);
			}
			List<string> list2 = new List<string>();
			MethodInfo[] methods = typeof(Smelter).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				string name = methodInfo.Name;
				if (name.IndexOf("Ore", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Queue", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Add", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Process", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Empty", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Spawn", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					List<string> list3 = new List<string>();
					ParameterInfo[] parameters = methodInfo.GetParameters();
					foreach (ParameterInfo parameterInfo in parameters)
					{
						list3.Add(parameterInfo.ParameterType.Name + " " + parameterInfo.Name);
					}
					list2.Add(methodInfo.Name + "(" + string.Join(", ", list3.ToArray()) + ")");
				}
			}
			ZNetView val = Rf.Get<ZNetView>(k, "m_nview") ?? View((Component)(object)k);
			GreydwarfLaborerPlugin.Log.LogInfo((object)("[Kiln] '" + text + "' is a " + ((object)k).GetType().FullName + "; owner=" + ((Object)(object)val != (Object)null && val.IsValid() && val.IsOwner()) + "; accepts=" + string.Join("/", new List<string>(Accepts(k)).ToArray()) + "; max=" + KilnMax(k) + "; queued=" + KilnQueued(k) + "; processed=" + KilnProcessed(k)));
			GreydwarfLaborerPlugin.Log.LogInfo((object)("[Kiln] Smelter fields: " + string.Join(", ", list.ToArray())));
			GreydwarfLaborerPlugin.Log.LogInfo((object)("[Kiln] Smelter methods: " + string.Join("; ", list2.ToArray())));
		}

		private static HashSet<string> OutputItems()
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			string[] array = KilnOutput.Value.Split(',');
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.Length > 0)
				{
					hashSet.Add(text2);
				}
			}
			return hashSet;
		}

		private static Vector3 KilnOutputPoint(Smelter k)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_002d: Unknown result type (might be due to invalid IL or missing references)
			Transform val = Rf.Get<Transform>(k, "m_outputPoint");
			return ((Object)(object)val != (Object)null) ? val.position : ((Component)k).transform.position;
		}

		private static ItemDrop NearestOutput(Smelter k, Vector3 from, HashSet<string> names, WorkState st)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			ItemDrop result = null;
			float num = float.MaxValue;
			Collider[] array = Physics.OverlapSphere(KilnOutputPoint(k), 4f, -1, (QueryTriggerInteraction)2);
			foreach (Collider val in array)
			{
				ItemDrop val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentInParent<ItemDrop>() : null);
				if (!((Object)(object)val2 == (Object)null) && names.Contains(LaborerSetup.CleanName(((Component)val2).gameObject)) && (st == null || !st.Ignore.TryGetValue(((Object)((Component)val2).gameObject).GetInstanceID(), out var value) || !(Time.time < value)))
				{
					float num2 = Vector3.Distance(from, ((Component)val2).transform.position);
					if (num2 < num)
					{
						num = num2;
						result = val2;
					}
				}
			}
			return result;
		}

		private static Smelter FindKilnAt(Vector3 pos)
		{
			//IL_0002: 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)
			Collider[] array = Physics.OverlapSphere(pos, 1.5f, -1, (QueryTriggerInteraction)2);
			foreach (Collider val in array)
			{
				Smelter val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentInParent<Smelter>() : null);
				if ((Object)(object)val2 != (Object)null && Flat(((Component)val2).transform.position, pos) < 0.5f)
				{
					return val2;
				}
			}
			return null;
		}

		private static bool ZoneLoaded(Vector3 pos)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.IsZoneLoaded(pos);
			}
			catch
			{
				return false;
			}
		}

		private static string PosKey(Vector3 v)
		{
			//IL_000a: 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)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			return string.Format(CultureInfo.InvariantCulture, "{0:F1},{1:F1},{2:F1}", v.x, v.y, v.z);
		}

		private static int CarriedFor(WorkState st, HashSet<string> items)
		{
			int num = 0;
			foreach (KeyValuePair<string, int> item in st.Carry)
			{
				if (items.Contains(item.Key))
				{
					num += item.Value;
				}
			}
			return num;
		}

		private static int ChestWood(Container chest, HashSet<string> items)
		{
			Inventory val = (((Object)(object)chest != (Object)null) ? chest.GetInventory() : null);
			if (val == null)
			{
				return 0;
			}
			int num = 0;
			foreach (ItemData allItem in val.GetAllItems())
			{
				if ((Object)(object)allItem.m_dropPrefab != (Object)null && items.Contains(((Object)allItem.m_dropPrefab).name))
				{
					num += allItem.m_stack;
				}
			}
			return num;
		}

		internal static List<Vector3> LoadKilns(ZDO zdo)
		{
			return LoadPositions(zdo, "SmallHands_Kilns");
		}

		private static List<Vector3> LoadPositions(ZDO zdo, string key)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			string text = ((zdo != null) ? zdo.GetString(key, "") : "");
			if (string.IsNullOrEmpty(text))
			{
				return list;
			}
			string[] array = text.Split(';');
			foreach (string text2 in array)
			{
				string[] array2 = text2.Split('|');
				if (array2.Length == 3 && float.TryParse(array2[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
				{
					list.Add(new Vector3(result, result2, result3));
				}
			}
			return list;
		}

		private static void SaveKilns(ZDO zdo, List<Vector3> kilns)
		{
			SavePositions(zdo, "SmallHands_Kilns", kilns);
		}

		private static void SavePositions(ZDO zdo, string key, List<Vector3> list)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			List<string> list2 = new List<string>();
			foreach (Vector3 item in list)
			{
				list2.Add(string.Format(CultureInfo.InvariantCulture, "{0:R}|{1:R}|{2:R}", item.x, item.y, item.z));
			}
			zdo.Set(key, string.Join(";", list2.ToArray()));
		}

		internal static bool IsKilnAssigned(MonsterAI follower, Smelter k)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			ZDO zDO = GetZDO((Component)(object)follower);
			Vector3 pos = ((Component)k).transform.position;
			return LoadKilns(zDO).Exists((Vector3 v) => Flat(v, pos) < 0.5f);
		}

		internal static void AssignKiln(Player p, Smelter k)
		{
			//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_0108: Unknown result type (might be due to invalid IL or missing references)
			MonsterAI val = FindFollower(p);
			if ((Object)(object)val == (Object)null)
			{
				Msg(p, "No tamed Greydwarf is following you");
				return;
			}
			if (!IsWoodKiln(k))
			{
				Msg(p, "A Greydwarf can only tend kilns");
				return;
			}
			Character component = ((Component)val).GetComponent<Character>();
			ZNetView val2 = View((Component)(object)component);
			if (!((Object)(object)val2 == (Object)null) && val2.IsValid())
			{
				val2.ClaimOwnership();
				ZDO zDO = val2.GetZDO();
				List<Vector3> list = LoadKilns(zDO);
				Vector3 pos = ((Component)k).transform.position;
				int num = list.FindIndex((Vector3 v) => Flat(v, pos) < 0.5f);
				if (num >= 0)
				{
					list.RemoveAt(num);
					SaveKilns(zDO, list);
					Msg(p, "Kiln unassigned (" + list.Count + " assigned)");
					return;
				}
				list.Add(pos);
				SaveKilns(zDO, list);
				Msg(p, "Kiln assigned (" + list.Count + " total). Alt + E on " + (zDO.GetBool("SmallHands_HasChest", false) ? "the" : "a") + " chest to start work.");
			}
		}

		internal static bool HasAnyAssignment(Character c)
		{
			ZDO zDO = GetZDO((Component)(object)c);
			if (zDO == null)
			{
				return false;
			}
			return zDO.GetBool("SmallHands_Working", false) || zDO.GetBool("SmallHands_HasTree", false) || zDO.GetBool("SmallHands_HasChest", false) || !string.IsNullOrEmpty(zDO.GetString("SmallHands_Kilns", "")) || !string.IsNullOrEmpty(zDO.GetString("SmallHands_Smelters", ""));
		}

		internal static void UnassignAll(Player p, Character c)
		{
			//IL_0061: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
			ZNetView val = View((Component)(object)c);
			if (!((Object)(object)val == (Object)null) && val.IsValid())
			{
				val.ClaimOwnership();
				ZDO zDO = val.GetZDO();
				zDO.Set("SmallHands_Working", false);
				zDO.Set("SmallHands_HasTree", false);
				zDO.Set("SmallHands_HasChest", false);
				zDO.Set("SmallHands_TreePos", Vector3.zero);
				zDO.Set("SmallHands_ChestPos", Vector3.zero);
				zDO.Set("SmallHands_Kilns", "");
				zDO.Set("SmallHands_Smelters", "");
				zDO.Set("SmallHands_Pending", "");
				States.Remove(c);
				MonsterAI component = ((Component)c).GetComponent<MonsterAI>();
				if ((Object)(object)component != (Object)null)
				{
					ForceUnfollow(component, c);
					Stop((BaseAI)(object)component);
				}
				FollowBehavior.SetHome(c, ((Component)c).transform.position);
				_cacheUntil = 0f;
				Msg(p, "Greydwarf unassigned. It will stay here.");
			}
		}

		private static bool KilnTick(MonsterAI ai, Character c, ZDO zdo, WorkState st, float dt, bool hasTree)
		{
			if (st.KilnJob == 0)
			{
				st.KilnScan -= dt;
				if (st.KilnScan > 0f)
				{
					return false;
				}
				st.KilnScan = 2f;
				PlanStationJob(c, zdo, st, hasTree);
				if (st.KilnJob == 0)
				{
					return false;
				}
				ResetProgress(st);
			}
			if (st.KilnJob == 1 || st.KilnJob == 5)
			{
				FetchForKilns(ai, c, zdo, st, dt);
			}
			else if (st.KilnJob == 3)
			{
				CollectOutput(ai, c, zdo, st, dt);
			}
			else if (st.KilnJob == 4)
			{
				StoreOutput(ai, c, zdo, st, dt);
			}
			else if (st.KilnJob == 6)
			{
				DeliverToSmelter(ai, c, zdo, st, dt);
			}
			else
			{
				DeliverToKiln(ai, c, zdo, st, dt);
			}
			return true;
		}

		private static void PlanKilnJob(Character c, ZDO zdo, WorkState st, bool hasTree)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: 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_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_030f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0314: Unknown result type (might be due to invalid IL or missing references)
			//IL_0326: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0421: Unknown result type (might be due to invalid IL or missing references)
			//IL_0422: Unknown result type (might be due to invalid IL or missing references)
			//IL_0427: Unknown result type (might be due to invalid IL or missing references)
			//IL_0429: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cd: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = LoadKilns(zdo);
			Vector3 position = ((Component)c).transform.position;
			bool flag = zdo.GetBool("SmallHands_HasChest", false);
			HashSet<string> hashSet = OutputItems();
			if (list.Count > 0)
			{
				KilnLog("plan-" + list.Count, "Checking " + list.Count + " assigned kiln(s); carrying " + CarryTotal(st) + " item(s); chest assigned: " + flag + ".");
			}
			if (flag && hashSet.Count > 0 && CarryCount(st, hashSet) > 0 && Time.time >= st.StoreRetryAt)
			{
				st.KilnJob = 4;
				return;
			}
			Smelter val = null;
			Smelter val2 = null;
			float num = float.MaxValue;
			float num2 = float.MaxValue;
			int num3 = 0;
			bool flag2 = false;
			for (int num4 = list.Count - 1; num4 >= 0; num4--)
			{
				Vector3 val3 = list[num4];
				Smelter val4 = FindKilnAt(val3);
				float value;
				if ((Object)(object)val4 == (Object)null)
				{
					if (ZoneLoaded(val3) && Flat(position, val3) < 30f)
					{
						list.RemoveAt(num4);
						flag2 = true;
						GreydwarfLaborerPlugin.Log.LogInfo((object)"An assigned kiln no longer exists; removed it.");
					}
				}
				else if (!st.KilnCooldown.TryGetValue(PosKey(val3), out value) || !(Time.time < value))
				{
					float num5 = Flat(position, val3);
					bool flag3 = KilnEmpty(val4);
					if (flag3 && flag && hashSet.Count > 0 && (Object)(object)NearestOutput(val4, position, hashSet, st) != (Object)null)
					{
						if (num5 < num2)
						{
							num2 = num5;
							val2 = val4;
						}
					}
					else
					{
						int num6 = KilnSpace(val4);
						if (num6 <= 0)
						{
							KilnLog("full-" + PosKey(val3), "A kiln is full.");
						}
						else if (!KilnTopUp.Value && !flag3)
						{
							KilnLog("burning-" + PosKey(val3), "A kiln still has " + KilnQueued(val4) + " wood queued; waiting for it to burn out (turn on 'Top up running kilns' to load it sooner).");
						}
						else if (num6 >= Mathf.Min(KilnRefillAt.Value, KilnMax(val4)))
						{
							num3 += num6;
							if (num5 < num)
							{
								num = num5;
								val = val4;
							}
						}
					}
				}
			}
			if (flag2)
			{
				SaveKilns(zdo, list);
			}
			if ((Object)(object)val2 != (Object)null)
			{
				st.KilnJob = 3;
				st.KilnPos = ((Component)val2).transform.position;
				st.HitPending = false;
				KilnLog("coal-" + PosKey(st.KilnPos), "Kiln is empty: collecting its coal.");
			}
			else
			{
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				HashSet<string> hashSet2 = KilnWood(val);
				if (hashSet2.Count == 0)
				{
					KilnLog("nowood-set", "The kiln doesn't accept any of the items in 'Kiln wood items'.");
					return;
				}
				int num7 = CarriedFor(st, hashSet2);
				int num8 = KilnSpace(val);
				int num9 = Mathf.Min(KilnRefillAt.Value, num8);
				if (num7 >= num9)
				{
					st.KilnJob = 2;
					st.KilnPos = ((Component)val).transform.position;
					st.HitPending = false;
					return;
				}
				if (!flag || Time.time < st.ChestEmptyUntil)
				{
					KilnLog("nochest", flag ? "Waiting: not enough wood in the chest or pack yet." : "A kiln needs wood but no chest is assigned (Alt + E on a chest).");
					return;
				}
				Vector3 vec = zdo.GetVec3("SmallHands_ChestPos", position);
				Container val5 = FindChestAt(vec);
				if ((Object)(object)val5 != (Object)null && num7 + ChestWood(val5, hashSet2) < num9)
				{
					st.ChestEmptyUntil = Time.time + 10f;
					KilnLog("lowwood", "Not enough wood for a kiln load (" + (num7 + ChestWood(val5, hashSet2)) + " of " + num9 + " needed).");
				}
				else
				{
					st.KilnJob = 1;
					st.KilnPos = ((Component)val).transform.position;
					st.FetchWant = Mathf.Max(1, num3 - num7);
					st.HitPending = false;
				}
			}
		}

		private static bool ClaimChest(Container chest, WorkState st, float dt)
		{
			st.WaitTimer -= dt;
			if (st.WaitTimer > 0f)
			{
				return false;
			}
			ZNetView val = Rf.Get<ZNetView>(chest, "m_nview") ?? View((Component)(object)chest);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return false;
			}
			if (!val.IsOwner())
			{
				bool flag = false;
				if (InUseM != null)
				{
					try
					{
						flag = (bool)InUseM.Invoke(chest, null);
					}
					catch
					{
					}
				}
				if (flag)
				{
					st.WaitTimer = 3f;
					return false;
				}
				val.ClaimOwnership();
			}
			return true;
		}

		private static void FetchForKilns(MonsterAI ai, Character c, ZDO zdo, WorkState st, float dt)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)c).transform.position;
			Vector3 vec = zdo.GetVec3("SmallHands_ChestPos", position);
			Container val = FindChestAt(vec);
			if ((Object)(object)val == (Object)null)
			{
				if (ZoneLoaded(vec) && Flat(position, vec) < 30f)
				{
					st.KilnJob = 0;
				}
				else
				{
					MoveTo((BaseAI)(object)ai, dt, vec, 1.7600001f, Flat(position, vec) > 8f);
				}
				return;
			}
			GameObject gameObject = ((Component)val).gameObject;
			Vector3 val2 = ClosestPoint(gameObject, position);
			float num = Flat(position, val2);
			if (num > 2.2f && !IsStuck(st, gameObject, num, dt))
			{
				MoveTo((BaseAI)(object)ai, dt, val2, 1.7600001f, num > 8f);
				return;
			}
			Stop((BaseAI)(object)ai);
			LookAt((BaseAI)(object)ai, gameObject.transform.position);
			if (!ClaimChest(val, st, dt))
			{
				return;
			}
			Inventory inventory = val.GetInventory();
			Smelter val3 = FindKilnAt(st.KilnPos);
			if (inventory == null || (Object)(object)val3 == (Object)null)
			{
				st.KilnJob = 0;
				return;
			}
			HashSet<string> hashSet = StationItems(val3);
			int fetchWant = st.FetchWant;
			int num2 = 0;
			foreach (ItemData item in new List<ItemData>(inventory.GetAllItems()))
			{
				if (num2 >= fetchWant)
				{
					break;
				}
				string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
				if (text != null && hashSet.Contains(text))
				{
					int num3 = Mathf.Min(item.m_stack, fetchWant - num2);
					if (inventory.RemoveItem(item, num3))
					{
						st.Carry.TryGetValue(text, out var value);
						st.Carry[text] = value + num3;
						num2 += num3;
					}
				}
			}
			SaveCarry(zdo, st);
			ResetProgress(st);
			if (num2 == 0)
			{
				st.ChestEmptyUntil = Time.time + 10f;
				st.KilnJob = 0;
			}
			else
			{
				st.KilnJob = (IsWoodKiln(val3) ? 2 : 6);
			}
		}

		private static void DeliverToKiln(MonsterAI ai, Character c, ZDO zdo, WorkState st, float dt)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: 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_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)c).transform.position;
			Smelter val = FindKilnAt(st.KilnPos);
			if ((Object)(object)val == (Object)null)
			{
				if (ZoneLoaded(st.KilnPos) && Flat(position, st.KilnPos) < 30f)
				{
					st.KilnJob = 0;
				}
				else
				{
					MoveTo((BaseAI)(object)ai, dt, st.KilnPos, 1.7600001f, Flat(position, st.KilnPos) > 8f);
				}
				return;
			}
			GameObject gameObject = ((Component)val).gameObject;
			Vector3 val2 = ClosestPoint(gameObject, position);
			float num = Flat(position, val2);
			if (num > 2.2f && !IsStuck(st, gameObject, num, dt))
			{
				MoveTo((BaseAI)(object)ai, dt, val2, 1.7600001f, num > 8f);
				return;
			}
			Stop((BaseAI)(object)ai);
			LookAt((BaseAI)(object)ai, gameObject.transform.position);
			DumpSmelter(val);
			HashSet<string> hashSet = KilnWood(val);
			int num2 = KilnSpace(val);
			int num3 = 0;
			bool flag = false;
			int num4 = KilnQueued(val);
			foreach (string item in new List<string>(st.Carry.Keys))
			{
				if (num2 <= 0 || flag)
				{
					break;
				}
				if (!hashSet.Contains(item))
				{
					continue;
				}
				int num5 = Mathf.Min(num2, st.Carry[item]);
				int num6 = 0;
				for (int i = 0; i < num5; i++)
				{
					if (!AddToKiln(val, item))
					{
						flag = true;
						break;
					}
					num6++;
				}
				num2 -= num6;
				num3 += num6;
				int num7 = st.Carry[item] - num6;
				if (num7 > 0)
				{
					st.Carry[item] = num7;
				}
				else
				{
					st.Carry.Remove(item);
				}
			}
			SaveCarry(zdo, st);
			if (flag)
			{
				GreydwarfLaborerPlugin.Log.LogWarning((object)("[Kiln] Could not put wood into the kiln (it accepts: " + string.Join(", ", new List<string>(Accepts(val)).ToArray()) + "; it was carrying: " + CarryTotal(st) + "). Backing off for 30 seconds - see the other [Kiln] lines above."));
			}
			else
			{
				GreydwarfLaborerPlugin.Log.LogInfo((object)("[Kiln] Loaded " + num3 + " wood into a kiln (queue " + num4 + " -> " + KilnQueued(val) + ")."));
			}
			st.KilnCooldown[PosKey(st.KilnPos)] = Time.time + (flag ? 30f : 3f);
			st.KilnJob = 0;
			st.KilnScan = 0f;
			ResetProgress(st);
		}

		private static void CollectOutput(MonsterAI ai, Character c, ZDO zdo, WorkState st, float dt)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: 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_0110: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)c).transform.position;
			Smelter val = FindKilnAt(st.KilnPos);
			HashSet<string> hashSet = OutputItems();
			if ((Object)(object)val == (Object)null || hashSet.Count == 0 || !KilnEmpty(val))
			{
				st.KilnJob = 0;
				ResetProgress(st);
				return;
			}
			ItemDrop val2 = NearestOutput(val, position, hashSet, st);
			if ((Object)(object)val2 == (Object)null)
			{
				st.KilnCooldown[PosKey(st.KilnPos)] = Time.time + 2f;
				ResetProgress(st);
				st.KilnJob = 0;
				PlanSmelterJob(c, zdo, st, packOnly: true);
				if (st.KilnJob == 0 && CarryCount(st, hashSet) > 0)
				{
					st.KilnJob = 4;
				}
				return;
			}
			GameObject gameObject = ((Component)val2).gameObject;
			Vector3 val3 = ClosestPoint(gameObject, position);
			float num = Flat(position, val3);
			if (num > 1.3f && !IsStuck(st, gameObject, num, dt))
			{
				MoveTo((BaseAI)(object)ai, dt, val3, 1.04f, num > 8f);
				return;
			}
			st.Ignore[((Object)gameObject).GetInstanceID()] = Time.time + 30f;
			st.Target = gameObject;
			PickUp(zdo, st);
			ResetProgress(st);
		}

		private static void StoreOutput(MonsterAI ai, Character c, ZDO zdo, WorkState st, float dt)
		{
			HashSet<string> hashSet = OutputItems();
			if (hashSet.Count == 0 || CarryCount(st, hashSet) <= 0)
			{
				st.KilnJob = 0;
				ResetProgress(st);
			}
			else if (!zdo.GetBool("SmallHands_HasChest", false))
			{
				st.KilnJob = 0;
			}
			else
			{
				Deposit(ai, c, zdo, st, dt, hashSet);
			}
		}

		internal static void PublishStatus(MonsterAI ai, Character c)
		{
			ZDO zDO = GetZDO((Component)(object)c);
			if (zDO != null)
			{
				string text = StatusOf(ai, c, zDO);
				if (zDO.GetString("SmallHands_Status", "") != text)
				{
					zDO.Set("SmallHands_Status", text);
				}
			}
		}

		private static string StatusOf(MonsterAI ai, Character c, ZDO zdo)
		{
			if (!zdo.GetBool("SmallHands_Working", false))
			{
				return FollowBehavior.IsFollowing((BaseAI)(object)ai) ? "Following" : "Idle";
			}
			if ((Object)(object)Care.GetAttacker(c) != (Object)null)
			{
				return "Fighting back";
			}
			if (!States.TryGetValue(c, out var value) || value == null)
			{
				return "Starting work";
			}
			switch (value.KilnJob)
			{
			case 1:
				return "Fetching wood for kiln";
			case 2:
				return "Loading kiln";
			case 3:
				return "Collecting coal";
			case 4:
				return "Storing coal";
			case 5:
				return "Fetching coal for smelter";
			case 6:
				return "Loading smelter";
			default:
				if (value.Depositing)
				{
					return (value.WaitTimer > 0.5f) ? "Waiting at chest" : "Delivering wood";
				}
				if ((Object)(object)value.Target == (Object)null)
				{
					if (value.Pending.Exists((PendingTree pt) => pt.Felled))
					{
						return "Replanting";
					}
					if (!zdo.GetBool("SmallHands_HasTree", false))
					{
						return (LoadKilns(zdo).Count > 0) ? "Tending kilns" : "Tending smelters";
					}
					return "Looking for work";
				}
				return value.Kind switch
				{
					3 => "Chopping tree", 
					2 => "Chopping log", 
					4 => "Clearing stump", 
					1 => "Collecting wood", 
					_ => "Working", 
				};
			}
		}

		internal static void BindPlayerTreeConfig(ConfigFile config)
		{
			ProtectPlayerTrees = config.Bind<bool>("3 - Woodcutting", "Protect player-planted trees", true, "Greydwarfs never chop a tree that grew from a sapling a player planted (they also refuse to be assigned to one). Trees from saplings a Greydwarf replanted are still cut, and so are wild trees. Only trees that grow while this mod is installed can be recognised: a tree that had already grown before is treated as a wild one.");
		}

		internal static bool IsProtectedTree(Component tree)
		{
			try
			{
				if (ProtectPlayerTrees == null || !ProtectPlayerTrees.