Decompiled source of DvergrWorkers v0.5.0

DvergrWorkers.dll

Decompiled 5 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("DvergrWorkers")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Dvergr workers help around your base and defend it when it is under attack.")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0+4234aec8c31961fa23bb5cb260d90d719f41e04b")]
[assembly: AssemblyProduct("DvergrWorkers")]
[assembly: AssemblyTitle("DvergrWorkers")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.5.0.0")]
[module: UnverifiableCode]
[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 DvergrWorkers
{
	public class FarmerWorker : WorkerBrain
	{
		private enum Phase
		{
			Plan,
			Fetch,
			Walk,
			Plant,
			Harvest,
			Collect,
			Deposit
		}

		private const float PlantTime = 0.6f;

		private const float HarvestWait = 0.9f;

		private const float CollectInterval = 0.3f;

		private const int MaxCollectTries = 12;

		private const float DropRadius = 3f;

		private const int FullSlots = 2;

		private const float FineStep = 0.5f;

		private const float CoarseStep = 1f;

		private Phase _phase;

		private PlantType _batchType;

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

		private Pickable _harvest;

		private Vector3 _collectPos;

		private float _timer;

		private int _collectTries;

		private int _planted;

		private int _harvested;

		private int _handledLastRound;

		private bool _noSpots;

		private bool _noSupply;

		private readonly HashSet<PlantType> _loggedBiome = new HashSet<PlantType>();

		private readonly HashSet<PlantType> _failedTypes = new HashSet<PlantType>();

		private readonly HashSet<Pickable> _failedPickables = new HashSet<Pickable>();

		private readonly HashSet<string> _ours = new HashSet<string>();

		private readonly Dictionary<string, int> _plantedByFamily = new Dictionary<string, int>();

		private readonly List<Occupied> _plantedSpots = new List<Occupied>();

		private Phase _nextAfterWalk = Phase.Plant;

		internal override WorkerRole Role => WorkerRoles.Farmer;

		protected override string IdleStatus()
		{
			if (_handledLastRound <= 0)
			{
				return "Idle";
			}
			return "Idle (planted/harvested " + _handledLastRound + " last round)";
		}

		protected override void OnRoundEnded(bool finished)
		{
			_harvest = null;
			_batchSpots.Clear();
			_handledLastRound = _planted + _harvested;
		}

		internal override bool WantsItem(string sharedName)
		{
			return PlantCatalogue.Crops.Any((PlantType t) => t.RequiredShared == sharedName);
		}

		private int Supply(string sharedName)
		{
			return base.Logistics.Carried.CountItems(sharedName, -1, true) + base.Logistics.AvailableTo(sharedName);
		}

		private bool IsOurs(ItemData item)
		{
			if (item != null)
			{
				return _ours.Contains(item.m_shared.m_name);
			}
			return false;
		}

		private bool CarryingSeeds()
		{
			foreach (ItemData allItem in base.Logistics.Carried.GetAllItems())
			{
				if (IsOurs(allItem) && PlantCatalogue.IsSeedName(allItem.m_shared.m_name))
				{
					return true;
				}
			}
			return false;
		}

		private bool CarryingOther()
		{
			foreach (ItemData allItem in base.Logistics.Carried.GetAllItems())
			{
				if (IsOurs(allItem) && !PlantCatalogue.IsSeedName(allItem.m_shared.m_name))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsCultivatedSpot(Vector3 p, out Heightmap heightmap)
		{
			//IL_0001: 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)
			heightmap = Heightmap.FindHeightmap(p);
			if ((Object)(object)heightmap != (Object)null)
			{
				return heightmap.IsCultivated(p);
			}
			return false;
		}

		private List<Pickable> RipeCrops()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			List<Pickable> list = new List<Pickable>();
			Pickable[] array = Object.FindObjectsByType<Pickable>((FindObjectsSortMode)0);
			foreach (Pickable val in array)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.m_nview == (Object)null) && val.m_nview.IsValid() && !val.m_picked && !_failedPickables.Contains(val) && InJobArea(((Component)val).transform.position) && IsCultivatedSpot(((Component)val).transform.position, out var _))
				{
					list.Add(val);
				}
			}
			return list;
		}

		private Dictionary<string, int> CountFamilies()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (PlantType crop in PlantCatalogue.Crops)
			{
				dictionary[crop.Family] = 0;
			}
			Plant[] array = Object.FindObjectsByType<Plant>((FindObjectsSortMode)0);
			foreach (Plant val in array)
			{
				if ((Object)(object)val != (Object)null && InJobArea(((Component)val).transform.position))
				{
					PlantType plantType = PlantCatalogue.Of(((Component)val).gameObject);
					if (plantType != null && plantType.IsTree)
					{
						plantType = null;
					}
					if (plantType != null)
					{
						dictionary[plantType.Family]++;
					}
				}
			}
			Pickable[] array2 = Object.FindObjectsByType<Pickable>((FindObjectsSortMode)0);
			foreach (Pickable val2 in array2)
			{
				if ((Object)(object)val2 == (Object)null || (Object)(object)val2.m_itemPrefab == (Object)null || !InJobArea(((Component)val2).transform.position) || !IsCultivatedSpot(((Component)val2).transform.position, out var _))
				{
					continue;
				}
				string name = val2.m_itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_name;
				foreach (PlantType crop2 in PlantCatalogue.Crops)
				{
					if (crop2.YieldShared == name)
					{
						dictionary[crop2.Family]++;
						break;
					}
				}
			}
			foreach (KeyValuePair<string, int> item in _plantedByFamily)
			{
				if (dictionary.ContainsKey(item.Key))
				{
					dictionary[item.Key] += item.Value;
				}
			}
			return dictionary;
		}

		private List<Vector3> FindSpots(PlantType type, int max)
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0499: Unknown result type (might be due to invalid IL or missing references)
			//IL_049e: 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_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_040e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0413: 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_0451: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			//IL_0307: Unknown result type (might be due to invalid IL or missing references)
			//IL_0528: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_0381: Unknown result type (might be due to invalid IL or missing references)
			int mask = LayerMask.GetMask(new string[5] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid" });
			int mask2 = LayerMask.GetMask(new string[3] { "Default", "static_solid", "piece" });
			float value = WorkerConfig.JobRadius.Value;
			List<Vector3> list = new List<Vector3>();
			int num = 0;
			List<Occupied> list2 = new List<Occupied>(_plantedSpots);
			Plant[] array = Object.FindObjectsByType<Plant>((FindObjectsSortMode)0);
			foreach (Plant val in array)
			{
				if ((Object)(object)val != (Object)null && Utils.DistanceXZ(((Component)val).transform.position, base.Home) <= value + 5f)
				{
					list2.Add(new Occupied
					{
						Position = ((Component)val).transform.position,
						Radius = val.m_growRadius
					});
				}
			}
			Pickable[] array2 = Object.FindObjectsByType<Pickable>((FindObjectsSortMode)0);
			Heightmap heightmap;
			foreach (Pickable val2 in array2)
			{
				if ((Object)(object)val2 != (Object)null && Utils.DistanceXZ(((Component)val2).transform.position, base.Home) <= value + 5f && IsCultivatedSpot(((Component)val2).transform.position, out heightmap))
				{
					list2.Add(new Occupied
					{
						Position = ((Component)val2).transform.position,
						Radius = type.Plant.m_growRadius
					});
				}
			}
			Vector3 val3 = default(Vector3);
			Vector3 val5 = default(Vector3);
			for (float num2 = 0f - value; num2 <= value; num2 += 1f)
			{
				for (float num3 = 0f - value; num3 <= value; num3 += 1f)
				{
					((Vector3)(ref val3))..ctor(base.Home.x + num2, base.Home.y, base.Home.z + num3);
					if (!InJobArea(val3) || !IsCultivatedSpot(val3, out heightmap))
					{
						continue;
					}
					Vector2[] array3 = (Vector2[])(object)new Vector2[4]
					{
						Vector2.zero,
						new Vector2(0.5f, 0f),
						new Vector2(0f, 0.5f),
						new Vector2(0.5f, 0.5f)
					};
					foreach (Vector2 val4 in array3)
					{
						((Vector3)(ref val5))..ctor(val3.x + val4.x, 0f, val3.z + val4.y);
						val5.y = ZoneSystem.instance.GetGroundHeight(val5);
						if (!IsCultivatedSpot(val5, out heightmap))
						{
							continue;
						}
						if (!PlantCatalogue.BiomeAllows(type, val5))
						{
							num++;
						}
						else
						{
							if (Physics.CheckSphere(val5 + Vector3.up * 0.15f, type.Plant.m_growRadius + 0.2f, mask) || Physics.Raycast(val5 + Vector3.up * 0.2f, Vector3.up, 100f, mask2))
							{
								continue;
							}
							bool flag = false;
							foreach (Occupied item in list2)
							{
								flag |= Utils.DistanceXZ(item.Position, val5) < Mathf.Max(type.Plant.m_growRadius, item.Radius) + 0.4f;
							}
							if (!flag)
							{
								list.Add(val5);
							}
						}
					}
				}
			}
			if (list.Count == 0 && num > 0 && _loggedBiome.Add(type))
			{
				DvergrWorkersPlugin.Log.LogInfo((object)("Farmer: " + ((Object)type.Prefab).name + " cannot be planted here, wrong biome (" + ((object)Heightmap.FindBiome(base.Home)/*cast due to .constrained prefix*/).ToString() + " is not in plant biomes " + ((object)Unsafe.As<Biome, Biome>(ref type.Plant.m_biome)/*cast due to .constrained prefix*/).ToString() + (((int)type.Piece.m_onlyInBiome != 0) ? (", piece biomes " + ((object)Unsafe.As<Biome, Biome>(ref type.Piece.m_onlyInBiome)/*cast due to .constrained prefix*/).ToString()) : "") + ")"));
			}
			Vector3 here = ((Component)this).transform.position;
			list.Sort((Vector3 a, Vector3 b) => Vector3.Distance(a, here).CompareTo(Vector3.Distance(b, here)));
			List<Vector3> list3 = new List<Vector3>();
			foreach (Vector3 item2 in list)
			{
				bool flag2 = false;
				foreach (Vector3 item3 in list3)
				{
					flag2 |= Utils.DistanceXZ(item3, item2) < type.Plant.m_growRadius + 0.4f;
				}
				if (!flag2)
				{
					list3.Add(item2);
					if (list3.Count >= max)
					{
						break;
					}
				}
			}
			return list3;
		}

		private PlantType ChooseType(string family)
		{
			PlantType plantType = null;
			PlantType plantType2 = null;
			foreach (PlantType crop in PlantCatalogue.Crops)
			{
				if (!(crop.Family != family) && !_failedTypes.Contains(crop))
				{
					if (crop.Raw)
					{
						plantType2 = crop;
					}
					else
					{
						plantType = crop;
					}
				}
			}
			int num = ((plantType != null) ? Supply(plantType.RequiredShared) : 0);
			int num2 = ((plantType2 != null) ? Supply(plantType2.RequiredShared) : 0);
			if (plantType != null && num > WorkerConfig.SeedReserve.Value)
			{
				return plantType;
			}
			if (plantType2 != null && num2 >= plantType2.Amount)
			{
				return plantType2;
			}
			if (plantType != null && num >= plantType.Amount)
			{
				return plantType;
			}
			return null;
		}

		protected override bool BeginRound()
		{
			_planted = 0;
			_harvested = 0;
			_noSpots = false;
			_noSupply = false;
			_failedTypes.Clear();
			_loggedBiome.Clear();
			_failedPickables.Clear();
			_plantedByFamily.Clear();
			_plantedSpots.Clear();
			_batchSpots.Clear();
			_batchType = null;
			_harvest = null;
			if (!PlantCatalogue.Crops.Any())
			{
				base.IdleNote = "No crops found in the cultivator";
				return false;
			}
			bool flag = false;
			bool flag2 = RipeCrops().Count > 0 || CarryingOther() || CarryingSeeds();
			if (!flag2)
			{
				foreach (string item in OrderedFamilies(CountFamilies()))
				{
					PlantType plantType = ChooseType(item);
					if (plantType == null)
					{
						_noSupply = true;
						continue;
					}
					List<Vector3> list = FindSpots(plantType, 1);
					flag |= list.Count > 0;
					if (list.Count > 0)
					{
						flag2 = true;
						break;
					}
					_noSpots = true;
				}
			}
			if (!flag2)
			{
				_handledLastRound = 0;
				base.IdleNote = NothingToDoNote();
				return false;
			}
			base.Equipment.EquipTool();
			_phase = Phase.Plan;
			DvergrWorkersPlugin.Log.LogInfo((object)"Farmer round started");
			return true;
		}

		private string NothingToDoNote()
		{
			if (_noSupply && !_noSpots)
			{
				return "Waiting for seeds in a chest";
			}
			if (_noSpots && !_noSupply)
			{
				return "No free tilled soil to plant";
			}
			return null;
		}

		private static List<string> OrderedFamilies(Dictionary<string, int> counts)
		{
			List<string> list = new List<string>(counts.Keys);
			list.Sort((string a, string b) => counts[a].CompareTo(counts[b]));
			return list;
		}

		protected override bool RunTask(float dt)
		{
			return _phase switch
			{
				Phase.Plan => Plan(), 
				Phase.Fetch => Fetch(dt), 
				Phase.Walk => Walk(dt), 
				Phase.Plant => PlantNow(dt), 
				Phase.Harvest => HarvestNow(dt), 
				Phase.Collect => Collect(dt), 
				Phase.Deposit => Deposit(dt), 
				_ => false, 
			};
		}

		private bool Abort(string reason)
		{
			DvergrWorkersPlugin.Log.LogInfo((object)("Farmer stopping: " + reason));
			base.IdleNote = reason;
			EndRound(finished: false);
			return false;
		}

		private bool Plan()
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if ((CarryingOther() || CarryingSeeds()) && base.Logistics.Carried.GetEmptySlots() <= 2)
			{
				return StartDeposit();
			}
			if (_batchSpots.Count > 0 && _batchType != null && base.Logistics.Carried.CountItems(_batchType.RequiredShared, -1, true) >= _batchType.Amount)
			{
				return BeginWalk(_batchSpots[0], "Walking to plant " + ((Object)_batchType.Prefab).name.Replace("sapling_", ""), Phase.Plant);
			}
			_batchSpots.Clear();
			Pickable val = NearestRipe();
			if ((Object)(object)val != (Object)null)
			{
				_harvest = val;
				Pickable target = val;
				if (base.Navigator.Begin(((Object)val).name, (Vector3 from) => WorkerNavigator.ClosestPointOnColliders(((Component)target).gameObject, from)))
				{
					_phase = Phase.Walk;
					_nextAfterWalk = Phase.Harvest;
					SetStatus("Walking to ripe " + ((Object)val).name.Replace("Pickable_", "").Replace("(Clone)", ""));
				}
				else
				{
					_failedPickables.Add(val);
					_harvest = null;
				}
				return true;
			}
			if (StartBatch())
			{
				return true;
			}
			if (CarryingOther() || CarryingSeeds())
			{
				return StartDeposit();
			}
			if (_planted + _harvested == 0)
			{
				base.IdleNote = NothingToDoNote();
			}
			EndRound(finished: true);
			return false;
		}

		private Pickable NearestRipe()
		{
			//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)
			Pickable result = null;
			float num = float.MaxValue;
			foreach (Pickable item in RipeCrops())
			{
				float num2 = Vector3.Distance(((Component)item).transform.position, ((Component)this).transform.position);
				if (num2 < num)
				{
					num = num2;
					result = item;
				}
			}
			return result;
		}

		private bool StartBatch()
		{
			foreach (string item in OrderedFamilies(CountFamilies()))
			{
				PlantType plantType = ChooseType(item);
				if (plantType == null)
				{
					_noSupply = true;
					continue;
				}
				List<Vector3> list = FindSpots(plantType, WorkerConfig.PlantsPerTrip.Value);
				if (list.Count == 0)
				{
					_noSpots = true;
					continue;
				}
				int num = Supply(plantType.RequiredShared);
				int num2 = Math.Min(list.Count, num / plantType.Amount);
				if (num2 > 0)
				{
					_batchType = plantType;
					_batchSpots.Clear();
					_batchSpots.AddRange(list.GetRange(0, num2));
					_ours.Add(plantType.RequiredShared);
					int num3 = num2 * plantType.Amount - base.Logistics.Carried.CountItems(plantType.RequiredShared, -1, true);
					if (num3 <= 0)
					{
						_phase = Phase.Plan;
						return true;
					}
					string shared = plantType.RequiredShared;
					if (base.Logistics.BeginFetchAny((ItemData i) => i.m_shared.m_name == shared, num3))
					{
						_phase = Phase.Fetch;
						SetStatus("Fetching " + shared.TrimStart(new char[1] { '$' }).Replace("item_", "") + " to plant");
						return true;
					}
					DvergrWorkersPlugin.Log.LogInfo((object)("Farmer fetch failed: " + base.Logistics.Reason));
					_failedTypes.Add(plantType);
					_batchSpots.Clear();
				}
			}
			return false;
		}

		private bool Fetch(float dt)
		{
			switch (base.Logistics.Step(dt))
			{
			case LogisticsResult.Moving:
				return true;
			case LogisticsResult.Failed:
				DvergrWorkersPlugin.Log.LogInfo((object)("Farmer fetch failed: " + base.Logistics.Reason));
				if (_batchType != null)
				{
					_failedTypes.Add(_batchType);
				}
				break;
			}
			_phase = Phase.Plan;
			return true;
		}

		private bool BeginWalk(Vector3 spot, string status, Phase next)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if (!base.Navigator.Begin("spot", (Vector3 from) => spot))
			{
				DvergrWorkersPlugin.Log.LogInfo((object)"No reachable spot next to a planting spot, skipping");
				if (_batchSpots.Count > 0)
				{
					_batchSpots.RemoveAt(0);
				}
				_phase = Phase.Plan;
				return true;
			}
			_nextAfterWalk = next;
			_phase = Phase.Walk;
			SetStatus(status);
			return true;
		}

		private bool Walk(float dt)
		{
			if (_nextAfterWalk == Phase.Harvest && ((Object)(object)_harvest == (Object)null || _harvest.m_picked))
			{
				_harvest = null;
				_phase = Phase.Plan;
				return true;
			}
			switch (base.Navigator.Step(dt))
			{
			case NavResult.InReach:
			case NavResult.CloseEnough:
				_timer = ((_nextAfterWalk == Phase.Harvest) ? 0.2f : 0.6f);
				_phase = _nextAfterWalk;
				break;
			case NavResult.Failed:
				if (_nextAfterWalk == Phase.Harvest && (Object)(object)_harvest != (Object)null)
				{
					_failedPickables.Add(_harvest);
					_harvest = null;
				}
				else if (_batchSpots.Count > 0)
				{
					_batchSpots.RemoveAt(0);
				}
				_phase = Phase.Plan;
				break;
			}
			return true;
		}

		private bool PlantNow(float dt)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: 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_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: 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_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			((BaseAI)base.Ai).StopMoving();
			_timer -= dt;
			if (_timer > 0f)
			{
				return true;
			}
			if (_batchSpots.Count == 0 || _batchType == null)
			{
				_phase = Phase.Plan;
				return true;
			}
			Vector3 val = _batchSpots[0];
			_batchSpots.RemoveAt(0);
			PlantType batchType = _batchType;
			ItemData val2 = null;
			foreach (ItemData allItem in base.Logistics.Carried.GetAllItems())
			{
				if (allItem.m_shared.m_name == batchType.RequiredShared && allItem.m_stack >= batchType.Amount)
				{
					val2 = allItem;
					break;
				}
			}
			int mask = LayerMask.GetMask(new string[5] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid" });
			if (val2 != null && IsCultivatedSpot(val, out var _) && PlantCatalogue.BiomeAllows(batchType, val) && !Physics.CheckSphere(val + Vector3.up * 0.15f, batchType.Plant.m_growRadius + 0.2f, mask))
			{
				base.Logistics.Carried.RemoveItem(val2, batchType.Amount);
				Quaternion val3 = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f);
				GameObject val4 = Object.Instantiate<GameObject>(batchType.Prefab, val, val3);
				batchType.Piece.m_placeEffect.Create(val, val3, val4.transform, 1f, -1, default(ZDOID));
				base.Animation.PlaySwing();
				DvergrWorkersPlugin.Log.LogInfo((object)("Farmer planted " + ((Object)batchType.Prefab).name + " in " + ((object)Heightmap.FindBiome(val)/*cast due to .constrained prefix*/).ToString() + " (plant biomes " + ((object)Unsafe.As<Biome, Biome>(ref batchType.Plant.m_biome)/*cast due to .constrained prefix*/).ToString() + ", piece biomes " + ((object)Unsafe.As<Biome, Biome>(ref batchType.Piece.m_onlyInBiome)/*cast due to .constrained prefix*/).ToString() + ")"));
				_planted++;
				_plantedSpots.Add(new Occupied
				{
					Position = val,
					Radius = batchType.Plant.m_growRadius
				});
				_plantedByFamily[batchType.Family] = (_plantedByFamily.TryGetValue(batchType.Family, out var value) ? value : 0) + 1;
				SetStatus("Planting " + ((Object)batchType.Prefab).name.Replace("sapling_", ""));
			}
			_phase = Phase.Plan;
			return true;
		}

		private bool HarvestNow(float dt)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			((BaseAI)base.Ai).StopMoving();
			_timer -= dt;
			if (_timer > 0f)
			{
				return true;
			}
			if ((Object)(object)_harvest == (Object)null || _harvest.m_picked)
			{
				_harvest = null;
				_phase = Phase.Plan;
				return true;
			}
			_collectPos = ((Component)_harvest).transform.position;
			_harvest.m_nview.ClaimOwnership();
			_harvest.Interact(base.Humanoid, false, false);
			_harvested++;
			base.Animation.PlaySwing();
			_harvest = null;
			_collectTries = 0;
			_timer = 0.9f;
			_phase = Phase.Collect;
			SetStatus("Harvesting");
			return true;
		}

		private bool Collect(float dt)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			_timer -= dt;
			if (_timer > 0f)
			{
				return true;
			}
			_timer = 0.3f;
			ItemDrop val = null;
			foreach (ItemDrop s_instance in ItemDrop.s_instances)
			{
				if ((Object)(object)s_instance != (Object)null && s_instance.m_itemData != null && Vector3.Distance(((Component)s_instance).transform.position, _collectPos) < 3f)
				{
					val = s_instance;
					break;
				}
			}
			if ((Object)(object)val == (Object)null || ++_collectTries > 12)
			{
				_phase = Phase.Plan;
				return true;
			}
			val.RequestOwn();
			string name = val.m_itemData.m_shared.m_name;
			if (base.Humanoid.Pickup(((Component)val).gameObject, false, false))
			{
				_ours.Add(name);
				_collectTries = 0;
			}
			return true;
		}

		private bool StartDeposit()
		{
			if (CarryingOther())
			{
				if (!base.Logistics.BeginDeposit((ItemData i) => IsOurs(i) && !PlantCatalogue.IsSeedName(i.m_shared.m_name)))
				{
					return Abort(base.Logistics.Reason);
				}
				_phase = Phase.Deposit;
				SetStatus("Delivering crops to a chest");
				return true;
			}
			if (CarryingSeeds())
			{
				if (!base.Logistics.BeginDeposit((ItemData i) => IsOurs(i) && PlantCatalogue.IsSeedName(i.m_shared.m_name), ChestKind.Input))
				{
					EndRound(finished: true);
					return false;
				}
				_phase = Phase.Deposit;
				SetStatus("Returning seeds to a chest");
				return true;
			}
			_phase = Phase.Plan;
			return true;
		}

		private bool Deposit(float dt)
		{
			switch (base.Logistics.Step(dt))
			{
			case LogisticsResult.Moving:
				return true;
			case LogisticsResult.Failed:
				return Abort(base.Logistics.Reason);
			default:
				if (base.Logistics.Transferred == 0)
				{
					return Abort("Output chests are full");
				}
				return StartDeposit();
			}
		}
	}
	public class BlacksmithWorker : MachineTenderWorker
	{
		internal override WorkerRole Role => WorkerRoles.Blacksmith;

		protected override string AllowedMachines => WorkerConfig.BlacksmithMachines.Value;

		protected override string NoMachinesNote => "No kiln, smelter or furnace nearby";
	}
	public class BarleyWorker : MachineTenderWorker
	{
		internal override WorkerRole Role => WorkerRoles.Barley;

		protected override string AllowedMachines => WorkerConfig.BarleyMachines.Value;

		protected override string NoMachinesNote => "No windmill nearby";
	}
	public class FlaxWorker : MachineTenderWorker
	{
		internal override WorkerRole Role => WorkerRoles.Flax;

		protected override string AllowedMachines => WorkerConfig.FlaxMachines.Value;

		protected override string NoMachinesNote => "No spinning wheel nearby";
	}
	public class EitrWorker : MachineTenderWorker
	{
		internal override WorkerRole Role => WorkerRoles.Eitr;

		protected override string AllowedMachines => WorkerConfig.EitrMachines.Value;

		protected override string NoMachinesNote => "No eitr refinery nearby";
	}
	public abstract class MachineTenderWorker : WorkerBrain
	{
		private enum Phase
		{
			Plan,
			Fetch,
			Walk,
			Work,
			Deposit
		}

		private enum Stage
		{
			Empty,
			Collect,
			Feed
		}

		private sealed class Need
		{
			public string SharedName;

			public string PrefabName;

			public bool Fuel;

			public int Count;
		}

		private const float FeedInterval = 0.35f;

		private const float EmptyWait = 0.8f;

		private const float CollectInterval = 0.3f;

		private const int MaxCollectTries = 12;

		private const float OutputRadius = 3.5f;

		private const int FullSlots = 2;

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

		private Phase _phase;

		private Stage _stage;

		private Smelter _machine;

		private List<Need> _needs = new List<Need>();

		private int _needIndex;

		private bool _fetchActive;

		private bool _pendingOutput;

		private float _timer;

		private int _collectTries;

		private bool _starved;

		private string _missingFuel = "";

		private readonly HashSet<string> _missingOre = new HashSet<string>();

		private float _lastDiagnostic = -1000f;

		private bool _deliverFirst;

		private int _handled;

		private int _handledLastRound;

		private readonly HashSet<Smelter> _served = new HashSet<Smelter>();

		private readonly HashSet<Smelter> _unreachable = new HashSet<Smelter>();

		private readonly HashSet<string> _productNames = new HashSet<string>();

		private HashSet<string> _wanted = new HashSet<string>();

		private float _wantedAt = -1000f;

		protected abstract string AllowedMachines { get; }

		protected abstract string NoMachinesNote { get; }

		internal override bool WantsItem(string sharedName)
		{
			if (Time.time - _wantedAt > 10f)
			{
				_wantedAt = Time.time;
				HashSet<string> hashSet = new HashSet<string>();
				foreach (Smelter item in FindMachines())
				{
					foreach (ItemConversion item2 in item.m_conversion)
					{
						if ((Object)(object)item2.m_from != (Object)null)
						{
							hashSet.Add(item2.m_from.m_itemData.m_shared.m_name);
						}
					}
					if ((Object)(object)item.m_fuelItem != (Object)null && item.m_maxFuel > 0)
					{
						hashSet.Add(item.m_fuelItem.m_itemData.m_shared.m_name);
					}
				}
				_wanted = hashSet;
			}
			return _wanted.Contains(sharedName);
		}

		protected override string IdleStatus()
		{
			if (_handledLastRound <= 0)
			{
				return "Idle";
			}
			return "Idle (handled " + _handledLastRound + " items last round)";
		}

		protected override void OnRoundEnded(bool finished)
		{
			_machine = null;
			_handledLastRound = _handled;
		}

		private bool IsProduct(ItemData item)
		{
			if (item != null)
			{
				return _productNames.Contains(item.m_shared.m_name);
			}
			return false;
		}

		private bool CarryingProduct()
		{
			foreach (ItemData allItem in base.Logistics.Carried.GetAllItems())
			{
				if (IsProduct(allItem))
				{
					return true;
				}
			}
			return false;
		}

		private static string PrefabOf(Smelter machine)
		{
			ZNetView nview = machine.m_nview;
			if (!((Object)(object)nview != (Object)null))
			{
				return Utils.GetPrefabName(((Component)machine).gameObject);
			}
			return Utils.GetPrefabName(((Component)nview).gameObject);
		}

		private static bool IsValid(Smelter machine)
		{
			if ((Object)(object)machine != (Object)null && (Object)(object)machine.m_nview != (Object)null)
			{
				return machine.m_nview.IsValid();
			}
			return false;
		}

		private static string DisplayName(Smelter machine)
		{
			return PrefabOf(machine).Replace('_', ' ');
		}

		private static IEnumerable<string> OutputNames(Smelter machine)
		{
			foreach (ItemConversion item in machine.m_conversion)
			{
				if ((Object)(object)item.m_to != (Object)null)
				{
					yield return item.m_to.m_itemData.m_shared.m_name;
				}
			}
		}

		private List<Smelter> FindMachines()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			HashSet<string> hashSet = new HashSet<string>(AllowedMachines.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries));
			List<Smelter> list = new List<Smelter>();
			_productNames.Clear();
			Smelter[] array = Object.FindObjectsByType<Smelter>((FindObjectsSortMode)0);
			foreach (Smelter val in array)
			{
				if (!IsValid(val) || !InJobArea(((Component)val).transform.position))
				{
					continue;
				}
				string text = PrefabOf(val);
				if (!hashSet.Contains(text.Trim()))
				{
					if (LoggedUnknown.Add(text))
					{
						DvergrWorkersPlugin.Log.LogInfo((object)(Role.Name + " ignoring '" + text + "' (add it to " + Role.Name + ".Machines to have this worker tend it)"));
					}
					continue;
				}
				list.Add(val);
				foreach (string item in OutputNames(val))
				{
					_productNames.Add(item);
				}
			}
			return list;
		}

		private IEnumerable<ItemDrop> ProductDropsNear(Smelter machine)
		{
			if ((Object)(object)machine.m_outputPoint == (Object)null)
			{
				yield break;
			}
			HashSet<string> outputs = new HashSet<string>(OutputNames(machine));
			foreach (ItemDrop s_instance in ItemDrop.s_instances)
			{
				if ((Object)(object)s_instance != (Object)null && s_instance.m_itemData != null && outputs.Contains(s_instance.m_itemData.m_shared.m_name) && Vector3.Distance(((Component)s_instance).transform.position, machine.m_outputPoint.position) < 3.5f)
				{
					yield return s_instance;
				}
			}
		}

		private bool HasPendingOutput(Smelter machine)
		{
			if (machine.GetProcessedQueueSize() > 0)
			{
				return true;
			}
			using (IEnumerator<ItemDrop> enumerator = ProductDropsNear(machine).GetEnumerator())
			{
				if (enumerator.MoveNext())
				{
					_ = enumerator.Current;
					return true;
				}
			}
			return false;
		}

		private int Supply(string sharedName)
		{
			return base.Logistics.Carried.CountItems(sharedName, -1, true) + base.Logistics.AvailableTo(sharedName);
		}

		private List<Need> BuildNeeds(Smelter machine, Dictionary<string, int> fuelUsers = null)
		{
			List<Need> list = new List<Need>();
			if ((Object)(object)machine.m_fuelItem != (Object)null && machine.m_maxFuel > 0)
			{
				int num = Mathf.FloorToInt((float)machine.m_maxFuel - machine.GetFuel());
				string name = machine.m_fuelItem.m_itemData.m_shared.m_name;
				if (num > 0)
				{
					int num2 = Supply(name);
					int num3 = 1;
					if (fuelUsers != null && fuelUsers.TryGetValue(name, out var value))
					{
						num3 = Math.Max(1, value);
					}
					if (num2 > 0)
					{
						num2 = Math.Min(num2, (num2 + num3 - 1) / num3);
						list.Add(new Need
						{
							SharedName = name,
							PrefabName = ((Object)((Component)machine.m_fuelItem).gameObject).name,
							Fuel = true,
							Count = Math.Min(num, num2)
						});
					}
					else
					{
						_starved = true;
						_missingFuel = name.Replace("$item_", "");
					}
				}
			}
			if (machine.m_maxOre > 0)
			{
				int num4 = machine.m_maxOre - machine.GetQueueSize();
				bool flag = false;
				foreach (ItemConversion item in machine.m_conversion)
				{
					if (num4 <= 0)
					{
						break;
					}
					if (!((Object)(object)item.m_from == (Object)null))
					{
						string name2 = item.m_from.m_itemData.m_shared.m_name;
						int num5 = Math.Min(num4, Supply(name2));
						if (num5 > 0)
						{
							flag = true;
							num4 -= num5;
							list.Add(new Need
							{
								SharedName = name2,
								PrefabName = ((Object)((Component)item.m_from).gameObject).name,
								Count = num5
							});
						}
					}
				}
				if (!flag && num4 > 0)
				{
					_starved = true;
					foreach (ItemConversion item2 in machine.m_conversion)
					{
						if ((Object)(object)item2.m_from != (Object)null)
						{
							_missingOre.Add(item2.m_from.m_itemData.m_shared.m_name.Replace("$item_", ""));
						}
					}
				}
			}
			return list;
		}

		private static bool IsFuelAnywhere(List<Smelter> machines, string sharedName)
		{
			foreach (Smelter machine in machines)
			{
				if ((Object)(object)machine.m_fuelItem != (Object)null && machine.m_maxFuel > 0 && machine.m_fuelItem.m_itemData.m_shared.m_name == sharedName)
				{
					return true;
				}
			}
			return false;
		}

		private static string JoinNames(IEnumerable<string> names)
		{
			List<string> list = new List<string>();
			foreach (string name in names)
			{
				list.Add((name.EndsWith("seeds") && name.Length > 5) ? (name.Substring(0, name.Length - 5) + " seeds") : name);
			}
			if (list.Count <= 1)
			{
				return string.Join("", list);
			}
			return string.Join(", ", list.GetRange(0, list.Count - 1)) + " or " + list[list.Count - 1];
		}

		private string WaitingNote()
		{
			List<string> list = new List<string>();
			if (_missingFuel.Length > 0)
			{
				list.Add(_missingFuel);
			}
			if (_missingOre.Count > 0)
			{
				list.Add((_missingOre.Count <= 3) ? JoinNames(_missingOre) : "items");
			}
			return "Waiting for " + string.Join(" and ", list) + " in a chest";
		}

		private void LogSupply(List<Smelter> machines)
		{
			if (Time.time - _lastDiagnostic < 120f)
			{
				return;
			}
			_lastDiagnostic = Time.time;
			foreach (Smelter machine in machines)
			{
				List<string> list = new List<string>();
				foreach (ItemConversion item in machine.m_conversion)
				{
					if (!((Object)(object)item.m_from == (Object)null))
					{
						string shared = item.m_from.m_itemData.m_shared.m_name;
						Func<ItemData, bool> func = (ItemData i) => i.m_shared.m_name == shared;
						list.Add(shared + " carried " + base.Logistics.Carried.CountItems(shared, -1, true) + ", input " + base.Logistics.CountInChests(ChestKind.Input, func) + ", output " + base.Logistics.CountInChests(ChestKind.Output, func) + ", usable " + base.Logistics.CountAvailable(func));
					}
				}
				DvergrWorkersPlugin.Log.LogInfo((object)(Role.Name + " has nothing to do at " + PrefabOf(machine) + " (queue " + machine.GetQueueSize() + "/" + machine.m_maxOre + "): " + string.Join("; ", list)));
			}
		}

		protected override bool BeginRound()
		{
			_served.Clear();
			_unreachable.Clear();
			_starved = false;
			_missingFuel = "";
			_missingOre.Clear();
			_handled = 0;
			_machine = null;
			_fetchActive = false;
			List<Smelter> list = FindMachines();
			if (list.Count == 0)
			{
				_handledLastRound = 0;
				base.IdleNote = NoMachinesNote;
				return false;
			}
			_deliverFirst = CarryingProduct();
			bool flag = _deliverFirst;
			foreach (Smelter item in list)
			{
				if (HasPendingOutput(item) || BuildNeeds(item).Count > 0)
				{
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				_handledLastRound = 0;
				base.IdleNote = (_starved ? WaitingNote() : null);
				LogSupply(list);
				return false;
			}
			_phase = Phase.Plan;
			DvergrWorkersPlugin.Log.LogInfo((object)(Role.Name + " round started with " + list.Count + " machine(s)"));
			return true;
		}

		protected override bool RunTask(float dt)
		{
			return _phase switch
			{
				Phase.Plan => Plan(), 
				Phase.Fetch => Fetch(dt), 
				Phase.Walk => Walk(dt), 
				Phase.Work => Work(dt), 
				Phase.Deposit => Deposit(dt), 
				_ => false, 
			};
		}

		private bool Abort(string reason)
		{
			DvergrWorkersPlugin.Log.LogInfo((object)(Role.Name + " stopping: " + reason));
			base.IdleNote = reason;
			EndRound(finished: false);
			return false;
		}

		private bool Plan()
		{
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			List<Smelter> list = FindMachines();
			if (CarryingProduct() && (_deliverFirst || base.Logistics.Carried.GetEmptySlots() <= 2))
			{
				_deliverFirst = false;
				return StartDeposit();
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			HashSet<string> hashSet = new HashSet<string>();
			foreach (Smelter item in list)
			{
				if (!((Object)(object)item.m_fuelItem == (Object)null) && item.m_maxFuel > 0)
				{
					string name = item.m_fuelItem.m_itemData.m_shared.m_name;
					if (!_served.Contains(item) && !_unreachable.Contains(item) && Mathf.FloorToInt((float)item.m_maxFuel - item.GetFuel()) > 0)
					{
						dictionary[name] = ((!dictionary.TryGetValue(name, out var value)) ? 1 : (value + 1));
					}
				}
			}
			foreach (Smelter item2 in list)
			{
				if (!((Object)(object)item2.m_fuelItem == (Object)null) && item2.m_maxFuel > 0)
				{
					continue;
				}
				foreach (string item3 in OutputNames(item2))
				{
					hashSet.Add(item3);
				}
			}
			Smelter val = null;
			List<Need> needs = null;
			bool pendingOutput = false;
			int num = int.MaxValue;
			float num2 = float.MaxValue;
			foreach (Smelter item4 in list)
			{
				if (_served.Contains(item4) || _unreachable.Contains(item4))
				{
					continue;
				}
				bool flag = HasPendingOutput(item4);
				List<Need> list2 = BuildNeeds(item4, dictionary);
				if (!flag && list2.Count == 0)
				{
					continue;
				}
				bool flag2 = false;
				foreach (string item5 in OutputNames(item4))
				{
					flag2 |= hashSet.Contains(item5) && IsFuelAnywhere(list, item5);
				}
				int num3 = ((!flag2) ? 1 : 0);
				float num4 = Vector3.Distance(((Component)item4).transform.position, ((Component)this).transform.position);
				if (num3 < num || (num3 == num && num4 < num2))
				{
					num = num3;
					num2 = num4;
					val = item4;
					needs = list2;
					pendingOutput = flag;
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				_machine = val;
				_needs = needs;
				_pendingOutput = pendingOutput;
				_needIndex = 0;
				_fetchActive = false;
				_phase = Phase.Fetch;
				return true;
			}
			if (CarryingProduct())
			{
				return StartDeposit();
			}
			if (_handled == 0 && _starved)
			{
				base.IdleNote = WaitingNote();
			}
			EndRound(finished: true);
			return false;
		}

		private bool Fetch(float dt)
		{
			if (!IsValid(_machine))
			{
				_phase = Phase.Plan;
				return true;
			}
			if (_fetchActive)
			{
				switch (base.Logistics.Step(dt))
				{
				case LogisticsResult.Moving:
					return true;
				case LogisticsResult.Failed:
					DvergrWorkersPlugin.Log.LogInfo((object)(Role.Name + " fetch failed: " + base.Logistics.Reason));
					break;
				}
				_fetchActive = false;
				_needIndex++;
			}
			while (_needIndex < _needs.Count)
			{
				Need need = _needs[_needIndex];
				Func<ItemData, bool> match = (ItemData i) => i.m_shared.m_name == need.SharedName;
				int num = need.Count - base.Logistics.Carried.CountItems(need.SharedName, -1, true);
				if (num > 0 && base.Logistics.BeginFetchAny(match, num))
				{
					_fetchActive = true;
					SetStatus("Fetching " + need.SharedName.TrimStart(new char[1] { '$' }).Replace("item_", "") + " from a chest");
					return true;
				}
				_needIndex++;
			}
			return BeginWalk();
		}

		private bool BeginWalk()
		{
			_needs.RemoveAll((Need n) => base.Logistics.Carried.CountItems(n.SharedName, -1, true) == 0);
			foreach (Need need in _needs)
			{
				need.Count = Math.Min(need.Count, base.Logistics.Carried.CountItems(need.SharedName, -1, true));
			}
			if (_needs.Count == 0 && !_pendingOutput)
			{
				_served.Add(_machine);
				_phase = Phase.Plan;
				return true;
			}
			Smelter machine = _machine;
			if (!base.Navigator.Begin(PrefabOf(machine), (Vector3 from) => WorkerNavigator.ClosestPointOnColliders(((Component)machine.m_nview).gameObject, from)))
			{
				DvergrWorkersPlugin.Log.LogInfo((object)("No reachable spot next to " + PrefabOf(machine) + ", skipping"));
				_unreachable.Add(machine);
				_phase = Phase.Plan;
				return true;
			}
			_phase = Phase.Walk;
			SetStatus("Walking to the " + DisplayName(machine));
			return true;
		}

		private bool Walk(float dt)
		{
			if (!IsValid(_machine))
			{
				_phase = Phase.Plan;
				return true;
			}
			switch (base.Navigator.Step(dt))
			{
			case NavResult.InReach:
			case NavResult.CloseEnough:
				_machine.m_nview.ClaimOwnership();
				_phase = Phase.Work;
				_stage = Stage.Empty;
				_timer = 0.2f;
				SetStatus("Tending the " + DisplayName(_machine));
				break;
			case NavResult.Failed:
				_unreachable.Add(_machine);
				_phase = Phase.Plan;
				break;
			}
			return true;
		}

		private bool Work(float dt)
		{
			if (!IsValid(_machine))
			{
				_phase = Phase.Plan;
				return true;
			}
			((BaseAI)base.Ai).StopMoving();
			FaceMachine();
			_timer -= dt;
			if (_timer > 0f)
			{
				return true;
			}
			switch (_stage)
			{
			case Stage.Empty:
				if (_machine.GetProcessedQueueSize() > 0)
				{
					_machine.m_nview.InvokeRPC("RPC_EmptyProcessed", Array.Empty<object>());
					_timer = 0.8f;
				}
				_stage = Stage.Collect;
				_collectTries = 0;
				break;
			case Stage.Collect:
				CollectOne();
				break;
			case Stage.Feed:
				FeedOne();
				break;
			}
			return true;
		}

		private void CollectOne()
		{
			_timer = 0.3f;
			ItemDrop val = null;
			using (IEnumerator<ItemDrop> enumerator = ProductDropsNear(_machine).GetEnumerator())
			{
				if (enumerator.MoveNext())
				{
					val = enumerator.Current;
				}
			}
			if ((Object)(object)val == (Object)null || ++_collectTries > 12)
			{
				_stage = Stage.Feed;
				_timer = 0.1f;
				return;
			}
			val.RequestOwn();
			int stack = val.m_itemData.m_stack;
			if (base.Humanoid.Pickup(((Component)val).gameObject, false, false))
			{
				_handled += stack;
				_collectTries = 0;
			}
		}

		private void FeedOne()
		{
			_timer = 0.35f;
			Need need = _needs.Find((Need n) => n.Count > 0);
			if (need == null)
			{
				_served.Add(_machine);
				_phase = Phase.Plan;
				return;
			}
			ItemData val = null;
			foreach (ItemData allItem in base.Logistics.Carried.GetAllItems())
			{
				if (allItem.m_shared.m_name == need.SharedName)
				{
					val = allItem;
					break;
				}
			}
			if (val != null)
			{
				bool num;
				if (!need.Fuel)
				{
					if (_machine.GetQueueSize() >= _machine.m_maxOre)
					{
						goto IL_010b;
					}
					num = !_machine.IsItemAllowed(((Object)val.m_dropPrefab).name);
				}
				else
				{
					num = _machine.GetFuel() > (float)(_machine.m_maxFuel - 1);
				}
				if (!num)
				{
					base.Logistics.Carried.RemoveItem(val, 1);
					if (need.Fuel)
					{
						_machine.m_nview.InvokeRPC("RPC_AddFuel", Array.Empty<object>());
					}
					else
					{
						_machine.m_nview.InvokeRPC("RPC_AddOre", new object[2]
						{
							((Object)val.m_dropPrefab).name,
							val.m_cheated
						});
					}
					need.Count--;
					_handled++;
					base.Animation.PlaySwing();
					return;
				}
			}
			goto IL_010b;
			IL_010b:
			need.Count = 0;
		}

		private void FaceMachine()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((Component)_machine).transform.position - ((Component)this).transform.position;
			val.y = 0f;
			if (((Vector3)(ref val)).sqrMagnitude > 0.01f)
			{
				base.Character.SetLookDir(((Vector3)(ref val)).normalized, 0f);
				((BaseAI)base.Ai).LookTowards(val);
			}
		}

		private bool StartDeposit()
		{
			if (!CarryingProduct())
			{
				_phase = Phase.Plan;
				return true;
			}
			if (!base.Logistics.BeginDeposit(IsProduct))
			{
				return Abort(base.Logistics.Reason);
			}
			_phase = Phase.Deposit;
			SetStatus("Delivering product to a chest");
			return true;
		}

		private bool Deposit(float dt)
		{
			switch (base.Logistics.Step(dt))
			{
			case LogisticsResult.Moving:
				return true;
			case LogisticsResult.Failed:
				return Abort(base.Logistics.Reason);
			default:
				if (base.Logistics.Transferred == 0)
				{
					return Abort("Output chests are full");
				}
				if (CarryingProduct())
				{
					return StartDeposit();
				}
				_phase = Phase.Plan;
				return true;
			}
		}
	}
	internal sealed class PlantType
	{
		public GameObject Prefab;

		public Piece Piece;

		public Plant Plant;

		public string RequiredShared;

		public int Amount;

		public bool Raw;

		public bool IsTree;

		public string Family;

		public string YieldShared;

		public HashSet<string> GrownNames = new HashSet<string>();
	}
	internal struct Occupied
	{
		public Vector3 Position;

		public float Radius;
	}
	internal static class PlantCatalogue
	{
		private const string CultivatorPrefab = "Cultivator";

		private static List<PlantType> _all;

		public const float SpacingMargin = 0.4f;

		public static List<PlantType> All
		{
			get
			{
				if (_all == null)
				{
					Load();
				}
				return _all;
			}
		}

		public static IEnumerable<PlantType> Crops => All.Where((PlantType t) => !t.IsTree);

		public static IEnumerable<PlantType> Trees => All.Where((PlantType t) => t.IsTree);

		public static HashSet<string> TreeSeedNames => new HashSet<string>(Trees.Select((PlantType t) => t.RequiredShared));

		public static bool IsSeedName(string sharedName)
		{
			return sharedName.ToLowerInvariant().Contains("seed");
		}

		public static PlantType Of(GameObject go)
		{
			string name = Utils.GetPrefabName(go);
			return All.FirstOrDefault((PlantType t) => ((Object)t.Prefab).name == name || (t.IsTree && t.GrownNames.Contains(name)));
		}

		public static bool BiomeAllows(PlantType type, Vector3 p)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			Biome val = Heightmap.FindBiome(p);
			if ((int)type.Piece.m_onlyInBiome != 0 && (val & type.Piece.m_onlyInBiome) == 0)
			{
				return false;
			}
			return (val & type.Plant.m_biome) > 0;
		}

		private static void Load()
		{
			_all = new List<PlantType>();
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Cultivator");
			PieceTable val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_buildPieces : null);
			if ((Object)(object)val == (Object)null)
			{
				DvergrWorkersPlugin.Log.LogError((object)"Cultivator piece table not found");
				return;
			}
			foreach (GameObject piece in val.m_pieces)
			{
				Piece component = piece.GetComponent<Piece>();
				Plant component2 = piece.GetComponent<Plant>();
				if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || component2.m_grownPrefabs.Length == 0 || component2.m_attachDistance > 0f || component.m_resources.Length == 0 || (Object)(object)component.m_resources[0].m_resItem == (Object)null)
				{
					continue;
				}
				GameObject val2 = component2.m_grownPrefabs[0];
				string name = component.m_resources[0].m_resItem.m_itemData.m_shared.m_name;
				int amount = Math.Max(1, component.m_resources[0].m_amount);
				if ((Object)(object)val2.GetComponent<TreeBase>() != (Object)null)
				{
					_all.Add(new PlantType
					{
						Prefab = piece,
						Piece = component,
						Plant = component2,
						RequiredShared = name,
						Amount = amount,
						IsTree = true,
						Family = name,
						GrownNames = new HashSet<string>(component2.m_grownPrefabs.Select((GameObject g) => ((Object)g).name))
					});
					DvergrWorkersPlugin.Log.LogInfo((object)("Tree: " + ((Object)piece).name + " (needs " + name + ", room " + component2.m_growRadius + "m, grows " + string.Join("/", component2.m_grownPrefabs.Select((GameObject g) => ((Object)g).name)) + ")"));
					continue;
				}
				Pickable component3 = val2.GetComponent<Pickable>();
				if (!((Object)(object)component3 == (Object)null) && !((Object)(object)component3.m_itemPrefab == (Object)null))
				{
					string name2 = component3.m_itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_name;
					PlantType plantType = new PlantType
					{
						Prefab = piece,
						Piece = component,
						Plant = component2,
						RequiredShared = name,
						Amount = amount,
						Raw = (IsSeedName(name2) && !IsSeedName(name)),
						YieldShared = name2,
						Family = (IsSeedName(name) ? name2 : name)
					};
					_all.Add(plantType);
					DvergrWorkersPlugin.Log.LogInfo((object)("Farmer crop: " + ((Object)piece).name + " (needs " + name + ", room " + component2.m_growRadius + "m, yields " + name2 + (plantType.Raw ? ", raw" : "") + ", family " + plantType.Family + ")"));
				}
			}
		}
	}
	[BepInPlugin("javidahmed64592.dvergrworkers", "Dvergr Workers", "0.5.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class DvergrWorkersPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "javidahmed64592.dvergrworkers";

		public const string PluginName = "Dvergr Workers";

		public const string PluginVersion = "0.5.0";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			WorkerConfig.Bind(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("javidahmed64592.dvergrworkers");
			_harmony.PatchAll();
			PrefabManager.OnVanillaPrefabsAvailable += RegisterPrefabs;
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void RegisterPrefabs()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterPrefabs;
			try
			{
				WorkerPrefabs.RegisterAll();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failed to register Dvergr worker prefabs: " + ex));
			}
		}
	}
	internal static class WorkerConfig
	{
		public static ConfigEntry<float> JobRadius;

		public static ConfigEntry<float> RespawnDelay;

		public static ConfigEntry<float> ThreatRadius;

		public static ConfigEntry<int> TreesPerRound;

		public static ConfigEntry<int> StumpsPerRound;

		public static ConfigEntry<string> BlacksmithMachines;

		public static ConfigEntry<string> BarleyMachines;

		public static ConfigEntry<string> FlaxMachines;

		public static ConfigEntry<string> EitrMachines;

		public static ConfigEntry<bool> TakeFromOutputChests;

		public static ConfigEntry<string> RefuelerFuels;

		public static ConfigEntry<float> RefuelBelow;

		public static ConfigEntry<int> SeedReserve;

		public static ConfigEntry<int> PlantsPerTrip;

		public static ConfigEntry<int> MaxTrees;

		public static ConfigEntry<int> TreesPerTrip;

		public static ConfigEntry<float> TreeBuildingClearance;

		public static ConfigEntry<bool> WorkAtNight;

		public static ConfigEntry<int> InventoryWidth;

		public static ConfigEntry<int> InventoryHeight;

		public static void Bind(ConfigFile config)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Expected O, but got Unknown
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Expected O, but got Unknown
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Expected O, but got Unknown
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Expected O, but got Unknown
			//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ea: Expected O, but got Unknown
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Expected O, but got Unknown
			//IL_034f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0359: Expected O, but got Unknown
			//IL_0381: Unknown result type (might be due to invalid IL or missing references)
			//IL_038b: Expected O, but got Unknown
			//IL_03b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03be: Expected O, but got Unknown
			JobRadius = config.Bind<float>("Workers", "JobRadius", 30f, new ConfigDescription("Workers wander and perform tasks within this distance (metres) of their bed. A small tolerance is added for tasks.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>()));
			RespawnDelay = config.Bind<float>("Workers", "RespawnDelay", 30f, new ConfigDescription("Seconds after a worker is lost before its bed spawns a replacement.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 600f), Array.Empty<object>()));
			ThreatRadius = config.Bind<float>("Workers", "ThreatRadius", 25f, new ConfigDescription("Workers drop their task and defend themselves if an enemy comes within this distance (metres).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 80f), Array.Empty<object>()));
			WorkAtNight = config.Bind<bool>("Workers", "WorkAtNight", false, "If false, workers rest at night and only work in daylight.");
			TakeFromOutputChests = config.Bind<bool>("Workers", "TakeFromOutputChests", true, "Let workers take items from output chests when another worker nearby produces them, e.g. the blacksmith taking the woodcutter's wood. Other items in output chests are never touched.");
			InventoryWidth = config.Bind<int>("Workers", "InventoryWidth", 8, new ConfigDescription("Columns in a worker's own inventory.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 12), Array.Empty<object>()));
			InventoryHeight = config.Bind<int>("Workers", "InventoryHeight", 4, new ConfigDescription("Rows in a worker's own inventory.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>()));
			foreach (WorkerRole item in WorkerRoles.All)
			{
				item.BindConfig(config);
			}
			BlacksmithMachines = config.Bind<string>(WorkerRoles.Blacksmith.Name, "Machines", "charcoal_kiln,smelter,blastfurnace", "Comma-separated prefab names of the kilns, smelters and furnaces the blacksmith tends.");
			BarleyMachines = config.Bind<string>(WorkerRoles.Barley.Name, "Machines", "windmill", "Comma-separated prefab names of the windmills the barley worker tends.");
			FlaxMachines = config.Bind<string>(WorkerRoles.Flax.Name, "Machines", "piece_spinningwheel", "Comma-separated prefab names of the spinning wheels the flax worker tends.");
			EitrMachines = config.Bind<string>(WorkerRoles.Eitr.Name, "Machines", "eitrrefinery", "Comma-separated prefab names of the eitr refineries the eitr worker tends.");
			RefuelerFuels = config.Bind<string>(WorkerRoles.Refueler.Name, "Fuels", "Resin,Wood,Coal", "Comma-separated item prefab names the refueler may use. Fires that burn anything else are left alone.");
			RefuelBelow = config.Bind<float>(WorkerRoles.Refueler.Name, "RefuelBelow", 0.6f, new ConfigDescription("A fire is topped up once its fuel falls to this fraction of its maximum.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			SeedReserve = config.Bind<int>(WorkerRoles.Farmer.Name, "SeedReserve", 4, new ConfigDescription("The farmer plants raw ingredients (a carrot, a turnip...) to make more seeds once a crop's seeds fall to this many or fewer.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), Array.Empty<object>()));
			PlantsPerTrip = config.Bind<int>(WorkerRoles.Farmer.Name, "PlantsPerTrip", 8, new ConfigDescription("How many of one crop the farmer plants before moving on to the next, which keeps the crops evenly mixed.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>()));
			MaxTrees = config.Bind<int>(WorkerRoles.TreeFarmer.Name, "MaxTrees", 40, new ConfigDescription("The tree farmer stops planting once the job area holds this many trees and saplings, wild ones included.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500), Array.Empty<object>()));
			TreesPerTrip = config.Bind<int>(WorkerRoles.TreeFarmer.Name, "TreesPerTrip", 5, new ConfigDescription("How many of one species the tree farmer plants before moving on to the next.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>()));
			TreeBuildingClearance = config.Bind<float>(WorkerRoles.TreeFarmer.Name, "BuildingClearance", 8f, new ConfigDescription("Trees are never planted closer than this (metres) to any building piece.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 40f), Array.Empty<object>()));
			TreesPerRound = config.Bind<int>(WorkerRoles.Woodcutter.Name, "TreesPerRound", 3, new ConfigDescription("How many standing trees a woodcutter fells per round.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			StumpsPerRound = config.Bind<int>(WorkerRoles.Woodcutter.Name, "StumpsPerRound", 10, new ConfigDescription("How many leftover tree stumps a woodcutter clears per round. 0 leaves stumps alone.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), Array.Empty<object>()));
		}
	}
	public class RefuelerWorker : WorkerBrain
	{
		private enum Phase
		{
			Plan,
			Fetch,
			Walk,
			Work,
			Return
		}

		private const float AddInterval = 0.35f;

		private Phase _phase;

		private Fireplace _fire;

		private string _fuelShared;

		private int _toAdd;

		private float _timer;

		private bool _starved;

		private string _missingFuel = "";

		private int _refueledThisRound;

		private int _refueledLastRound;

		private readonly HashSet<Fireplace> _served = new HashSet<Fireplace>();

		private readonly HashSet<Fireplace> _unreachable = new HashSet<Fireplace>();

		private readonly HashSet<string> _fuelNames = new HashSet<string>();

		internal override WorkerRole Role => WorkerRoles.Refueler;

		protected override string IdleStatus()
		{
			if (_refueledLastRound <= 0)
			{
				return "Idle";
			}
			return "Idle (refuelled " + _refueledLastRound + " fires last round)";
		}

		protected override void OnRoundEnded(bool finished)
		{
			_fire = null;
			_refueledLastRound = _refueledThisRound;
		}

		internal override bool WantsItem(string sharedName)
		{
			string[] array = WorkerConfig.RefuelerFuels.Value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text.Trim());
				ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val != (Object)null && val.m_itemData.m_shared.m_name == sharedName)
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsValid(Fireplace fire)
		{
			if ((Object)(object)fire != (Object)null && (Object)(object)fire.m_nview != (Object)null)
			{
				return fire.m_nview.IsValid();
			}
			return false;
		}

		private static float Fuel(Fireplace fire)
		{
			return fire.m_nview.GetZDO().GetFloat(ZDOVars.s_fuel, 0f);
		}

		private static string PrefabOf(Fireplace fire)
		{
			return Utils.GetPrefabName(((Component)fire.m_nview).gameObject);
		}

		private static string FuelShared(Fireplace fire)
		{
			return fire.m_fuelItem.m_itemData.m_shared.m_name;
		}

		private bool IsFuel(ItemData item)
		{
			if (item != null)
			{
				return _fuelNames.Contains(item.m_shared.m_name);
			}
			return false;
		}

		private static int Room(Fireplace fire)
		{
			float num = Fuel(fire);
			float num2 = fire.m_maxFuel * WorkerConfig.RefuelBelow.Value;
			if (!(num <= num2))
			{
				return 0;
			}
			return Mathf.FloorToInt(fire.m_maxFuel - num);
		}

		private List<Fireplace> FindFires()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			HashSet<string> hashSet = new HashSet<string>(WorkerConfig.RefuelerFuels.Value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries));
			List<Fireplace> list = new List<Fireplace>();
			_fuelNames.Clear();
			Fireplace[] array = Object.FindObjectsByType<Fireplace>((FindObjectsSortMode)0);
			foreach (Fireplace val in array)
			{
				if (IsValid(val) && !val.m_infiniteFuel && val.m_canRefill && !((Object)(object)val.m_fuelItem == (Object)null) && !(val.m_secPerFuel <= 0f) && InJobArea(((Component)val).transform.position) && hashSet.Contains(((Object)((Component)val.m_fuelItem).gameObject).name.Trim()))
				{
					Piece componentInParent = ((Component)val).GetComponentInParent<Piece>();
					if (!((Object)(object)componentInParent == (Object)null) && componentInParent.IsPlacedByPlayer() && val.m_nview.GetZDO().GetInt(ZDOVars.s_state, 1) == 1)
					{
						list.Add(val);
						_fuelNames.Add(FuelShared(val));
					}
				}
			}
			return list;
		}

		private int Supply(string sharedName)
		{
			return base.Logistics.Carried.CountItems(sharedName, -1, true) + base.Logistics.AvailableTo(sharedName);
		}

		private bool CarryingFuel()
		{
			foreach (ItemData allItem in base.Logistics.Carried.GetAllItems())
			{
				if (IsFuel(allItem))
				{
					return true;
				}
			}
			return false;
		}

		protected override bool BeginRound()
		{
			_served.Clear();
			_unreachable.Clear();
			_starved = false;
			_missingFuel = "";
			_refueledThisRound = 0;
			_fire = null;
			List<Fireplace> list = FindFires();
			bool flag = false;
			foreach (Fireplace item in list)
			{
				if (Room(item) > 0)
				{
					if (Supply(FuelShared(item)) > 0)
					{
						flag = true;
						break;
					}
					_starved = true;
					_missingFuel = FuelName(item);
				}
			}
			if (!flag)
			{
				_refueledLastRound = 0;
				base.IdleNote = (_starved ? ("Waiting for " + _missingFuel + " in a chest") : null);
				return false;
			}
			base.Equipment.EquipTool();
			_phase = Phase.Plan;
			DvergrWorkersPlugin.Log.LogInfo((object)"Refueler round started");
			return true;
		}

		private static string FuelName(Fireplace fire)
		{
			return ((Object)((Component)fire.m_fuelItem).gameObject).name.ToLowerInvariant();
		}

		protected override bool RunTask(float dt)
		{
			return _phase switch
			{
				Phase.Plan => Plan(), 
				Phase.Fetch => Fetch(dt), 
				Phase.Walk => Walk(dt), 
				Phase.Work => Work(dt), 
				Phase.Return => Return(dt), 
				_ => false, 
			};
		}

		private bool Plan()
		{
			//IL_007a: 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)
			List<Fireplace> list = FindFires();
			Fireplace val = null;
			float num = float.MaxValue;
			foreach (Fireplace item in list)
			{
				if (_served.Contains(item) || _unreachable.Contains(item) || Room(item) <= 0)
				{
					continue;
				}
				if (Supply(FuelShared(item)) <= 0)
				{
					_starved = true;
					_missingFuel = FuelName(item);
					continue;
				}
				float num2 = Vector3.Distance(((Component)item).transform.position, ((Component)this).transform.position);
				if (num2 < num)
				{
					num = num2;
					val = item;
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				if (CarryingFuel())
				{
					return StartReturn();
				}
				if (_refueledThisRound == 0 && _starved)
				{
					base.IdleNote = "Waiting for " + _missingFuel + " in a chest";
				}
				EndRound(finished: true);
				return false;
			}
			_fire = val;
			_fuelShared = FuelShared(val);
			int num3 = 0;
			foreach (Fireplace item2 in list)
			{
				if (!_served.Contains(item2) && !_unreachable.Contains(item2) && FuelShared(item2) == _fuelShared)
				{
					num3 += Math.Max(0, Room(item2));
				}
			}
			int num4 = Math.Min(num3, Supply(_fuelShared)) - base.Logistics.Carried.CountItems(_fuelShared, -1, true);
			_toAdd = Math.Min(Room(val), base.Logistics.Carried.CountItems(_fuelShared, -1, true));
			if (num4 > 0)
			{
				string shared = _fuelShared;
				if (base.Logistics.BeginFetchAny((ItemData i) => i.m_shared.m_name == shared, num4))
				{
					_phase = Phase.Fetch;
					SetStatus("Fetching fuel from a chest");
					return true;
				}
				DvergrWorkersPlugin.Log.LogInfo((object)("Refueler fetch failed: " + base.Logistics.Reason));
			}
			return BeginWalk();
		}

		private bool Fetch(float dt)
		{
			switch (base.Logistics.Step(dt))
			{
			case LogisticsResult.Moving:
				return true;
			case LogisticsResult.Failed:
				DvergrWorkersPlugin.Log.LogInfo((object)("Refueler fetch failed: " + base.Logistics.Reason));
				break;
			}
			_toAdd = (IsValid(_fire) ? Math.Min(Room(_fire), base.Logistics.Carried.CountItems(_fuelShared, -1, true)) : 0);
			return BeginWalk();
		}

		private bool BeginWalk()
		{
			if (!IsValid(_fire) || _toAdd <= 0)
			{
				if ((Object)(object)_fire != (Object)null)
				{
					_unreachable.Add(_fire);
				}
				_phase = Phase.Plan;
				return true;
			}
			Fireplace fire = _fire;
			if (!base.Navigator.Begin(PrefabOf(fire), (Vector3 from) => WorkerNavigator.ClosestPointOnColliders(((Component)fire.m_nview).gameObject, from)))
			{
				DvergrWorkersPlugin.Log.LogInfo((object)("No reachable spot next to " + PrefabOf(fire) + ", skipping"));
				_unreachable.Add(fire);
				_phase = Phase.Plan;
				return true;
			}
			_phase = Phase.Walk;
			SetStatus("Walking to a " + PrefabOf(fire).Replace('_', ' '));
			return true;
		}

		private bool Walk(float dt)
		{
			if (!IsValid(_fire))
			{
				_phase = Phase.Plan;
				return true;
			}
			switch (base.Navigator.Step(dt))
			{
			case NavResult.InReach:
			case NavResult.CloseEnough:
				_fire.m_nview.ClaimOwnership();
				_phase = Phase.Work;
				_timer = 0.2f;
				SetStatus("Refuelling a " + PrefabOf(_fire).Replace('_', ' '));
				break;
			case NavResult.Failed:
				_unreachable.Add(_fire);
				_phase = Phase.Plan;
				break;
			}
			return true;
		}

		private bool Work(float dt)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			if (!IsValid(_fire))
			{
				_phase = Phase.Plan;
				return true;
			}
			((BaseAI)base.Ai).StopMoving();
			Vector3 val = ((Component)_fire).transform.position - ((Component)this).transform.position;
			val.y = 0f;
			if (((Vector3)(ref val)).sqrMagnitude > 0.01f)
			{
				base.Character.SetLookDir(((Vector3)(ref val)).normalized, 0f);
				((BaseAI)base.Ai).LookTowards(val);
			}
			_timer -= dt;
			if (_timer > 0f)
			{
				return true;
			}
			_timer = 0.35f;
			ItemData val2 = null;
			foreach (ItemData allItem in base.Logistics.Carried.GetAllItems())
			{
				if (allItem.m_shared.m_name == _fuelShared)
				{
					val2 = allItem;
					break;
				}
			}
			if (val2 == null || _toAdd <= 0 || (float)Mathf.CeilToInt(Fuel(_fire)) >= _fire.m_maxFuel)
			{
				_served.Add(_fire);
				_phase = Phase.Plan;
				return true;
			}
			base.Logistics.Carried.RemoveItem(val2, 1);
			_fire.m_nview.InvokeRPC("RPC_AddFuel", Array.Empty<object>());
			_toAdd--;
			_refueledThisRound++;
			return true;
		}

		private bool StartReturn()
		{
			if (!base.Logistics.BeginDeposit(IsFuel, ChestKind.Input))
			{
				EndRound(finished: true);
				return false;
			}
			_phase = Phase.Return;
			SetStatus("Returning spare fuel to a chest");
			return true;
		}

		private bool Return(float dt)
		{
			switch (base.Logistics.Step(dt))
			{
			case LogisticsResult.Moving:
				return true;
			case LogisticsResult.Failed:
				EndRound(finished: true);
				return false;
			default:
				if (base.Logistics.Transferred > 0 && CarryingFuel())
				{
					return StartReturn();
				}
				EndRound(finished: true);
				return false;
			}
		}
	}
	public class RepairWorker : WorkerBrain
	{
		private enum Phase
		{
			Walking,
			Windup,
			Recover
		}

		private const float WindupTime = 0.5f;

		private const float RecoverTime = 1.1f;

		private Phase _phase;

		private float _phaseTimer;

		private readonly List<WearNTear> _queue = new List<WearNTear>();

		private readonly HashSet<WearNTear> _failed = new HashSet<WearNTear>();

		private WearNTear _current;

		private int _repairedThisRound;

		private int _repairedLastRound;

		internal override WorkerRole Role => WorkerRoles.Repair;

		protected override string IdleStatus()
		{
			if (_repairedLastRound <= 0)
			{
				return "Idle";
			}
			return "Idle (repaired " + _repairedLastRound + " pieces last round)";
		}

		protected override void OnRoundEnded(bool finished)
		{
			_queue.Clear();
			_current = null;
			_repairedLastRound = _repairedThisRound;
		}

		protected override bool BeginRound()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			_queue.Clear();
			_failed.Clear();
			_repairedThisRound = 0;
			foreach (WearNTear allInstance in WearNTear.GetAllInstances())
			{
				if (NeedsRepair(allInstance) && InJobArea(((Component)allInstance).transform.position))
				{
					_queue.Add(allInstance);
				}
			}
			if (_queue.Count == 0)
			{
				_repairedLastRound = 0;
				return false;
			}
			base.Equipment.EquipTool();
			_current = null;
			_phase = Phase.Walking;
			DvergrWorkersPlugin.Log.LogInfo((object)("Repair round started with " + _queue.Count + " damaged pieces"));
			return true;
		}

		private static bool NeedsRepair(WearNTear wnt)
		{
			if ((Object)(object)wnt == (Object)null || (Object)(object)wnt.m_nview == (Object)null || !wnt.m_nview.IsValid())
			{
				return false;
			}
			Piece component = ((Component)wnt).GetComponent<Piece>();
			if ((Object)(object)component == (Object)null || !component.IsPlacedByPlayer())
			{
				return false;
			}
			return wnt.GetHealthPercentage() < 0.999f;
		}

		protected override bool RunTask(float dt)
		{
			switch (_phase)
			{
			case Phase.Walking:
				return Walk(dt);
			case Phase.Windup:
				((BaseAI)base.Ai).StopMoving();
				FacePiece();
				_phaseTimer -= dt;
				if (_phaseTimer <= 0f)
				{
					Hammer();
					_phase = Phase.Recover;
					_phaseTimer = 1.1f;
				}
				return true;
			case Phase.Recover:
				((BaseAI)base.Ai).StopMoving();
				_phaseTimer -= dt;
				if (_phaseTimer <= 0f)
				{
					_current = null;
					_phase = Phase.Walking;
				}
				return true;
			default:
				return false;
			}
		}

		private bool Walk(float dt)
		{
			if ((Object)(object)_current == (Object)null && !PickNext())
			{
				EndRound(finished: true);
				return false;
			}
			if (!NeedsRepair(_current))
			{
				_current = null;
				return true;
			}
			switch (base.Navigator.Step(dt))
			{
			case NavResult.InReach:
			case NavResult.CloseEnough:
				_phase = Phase.Windup;
				_phaseTimer = 0.5f;
				break;
			case NavResult.Failed:
				_failed.Add(_current);
				_current = null;
				break;
			}
			return true;
		}

		private bool PickNext()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			while (_queue.Count > 0)
			{
				int num = -1;
				float num2 = float.MaxValue;
				for (int i = 0; i < _queue.Count; i++)
				{
					if (!((Object)(object)_queue[i] == (Object)null))
					{
						float num3 = Vector3.Distance(((Component)_queue[i]).transform.position, ((Component)this).transform.position);
						if (num3 < num2)
						{
							num2 = num3;
							num = i;
						}
					}
				}
				if (num < 0)
				{
					_queue.Clear();
					return false;
				}
				WearNTear candidate = _queue[num];
				_queue.RemoveAt(num);
				if (NeedsRepair(candidate) && !_failed.Contains(candidate))
				{
					if (base.Navigator.Begin(((Object)candidate).name, (Vector3 from) => WorkerNavigator.ClosestPointOnColliders(((Component)candidate).gameObject, from)))
					{
						_current = candidate;
						SetStatus("Walking to a damaged piece (" + (_queue.Count + 1) + " left)");
						return true;
					}
					DvergrWorkersPlugin.Log.LogInfo((object)("No reachable spot next to " + ((Object)candidate).name + ", skipping"));
				}
			}
			return false;
		}

		private void FacePiece()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_current == (Object)null))
			{
				Vector3 val = ((Component)_current).transform.position - ((Component)this).transform.position;
				val.y = 0f;
				if (((Vector3)(ref val)).sqrMagnitude > 0.01f)
				{
					base.Character.SetLookDir(((Vector3)(ref val)).normalized, 0f);
					((BaseAI)base.Ai).LookTowards(val);
				}
			}
		}

		private void Hammer()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			if (!NeedsRepair(_current))
			{
				return;
			}
			if (_current.Repair())
			{
				_repairedThisRound++;
				SetStatus("Repairing (" + _queue.Count + " more queued)");
				Piece component = ((Component)_current).GetComponent<Piece>();
				if ((Object)(object)component != (Object)null && component.m_placeEffect != null)
				{
					component.m_placeEffect.Create(((Component)component).transform.position, ((Component)component).transform.rotation, (Transform)null, 1f, -1, base.Character.GetZDOID());
				}
			}
			base.Animation.PlaySwing();
		}
	}
	public class TreeFarmerWorker : WorkerBrain
	{
		private enum Phase
		{
			Plan,
			Fetch,
			Walk,
			Plant,
			Return
		}

		private const float PlantTime = 0.6f;

		private const float GridStep = 2f;

		private const float TreeSpacingMargin = 1f;

		private const float DefaultTreeRoom = 2f;

		private const float MinWaterClearance = 0.5f;

		private const float FarmlandClearance = 2.5f;

		private Phase _phase;

		private PlantType _batchType;

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

		private float _timer;

		private int _planted;

		private int _plantedLastRound;

		private bool _noSupply;

		private bool _noSpots;

		private bool _full;

		private readonly HashSet<PlantType> _failedTypes = new HashSet<PlantType>();

		private readonly HashSet<PlantType> _loggedBiome = new HashSet<PlantType>();

		private readonly Dictionary<string, int> _plantedByFamily = new Dictionary<string, int>();

		private readonly List<Occupied> _plantedSpots = new List<Occupied>();

		internal override WorkerRole Role => WorkerRoles.TreeFarmer;

		protected override string IdleStatus()
		{
			if (_plantedLastRound <= 0)
			{
				return "Idle";
			}
			return "Idle (planted " + _plantedLastRound + " trees last round)";
		}

		protected override void OnRoundEnded(bool finished)
		{
			_batchSpots.Clear();
			_plantedLastRound = _planted;
		}

		internal override bool WantsItem(string sharedName)
		{
			return PlantCatalogue.TreeSeedNames.Contains(sharedName);
		}

		private int Supply(string sharedName)
		{
			return base.Logistics.Carried.CountItems(sharedName, -1, true) + base.Logistics.AvailableTo(sharedName);
		}

		private bool CarryingSeeds()
		{
			HashSet<string> treeSeedNames = PlantCatalogue.TreeSeedNames;
			foreach (ItemData allItem in base.Logistics.Carried.GetAllItems())
			{
				if (treeSeedNames.Contains(allItem.m_shared.m_name))
				{
					return true;
				}
			}
			return false;
		}

		private Dictionary<string, int> CountFamilies(out int total)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (PlantType tree in PlantCatalogue.Trees)
			{
				dictionary[tree.Family] = 0;
			}
			total = 0;
			TreeBase[] array = Object.FindObjectsByType<TreeBase>((FindObjectsSortMode)0);
			foreach (TreeBase val in array)
			{
				if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeInHierarchy && InJobArea(((Component)val).transform.position))
				{
					total++;
					PlantType plantType = PlantCatalogue.Of(((Component)val).gameObject);
					if (plantType != null && plantType.IsTree)
					{
						dictionary[plantType.Family]++;
					}
				}
			}
			Plant[] array2 = Object.FindObjectsByType<Plant>((FindObjectsSortMode)0);
			foreach (Plant val2 in array2)
			{
				if ((Object)(object)val2 != (Object)null && InJobArea(((Component)val2).transform.position))
				{
					PlantType plantType2 = PlantCatalogue.Of(((Component)val2).gameObject);
					if (plantType2 != null && plantType2.IsTree)
					{
						total++;
						dictionary[plantType2.Family]++;
					}
				}
			}
			foreach (KeyValuePair<string, int> item in _plantedByFamily)
			{
				if (dictionary.ContainsKey(item.Key))
				{
					dictionary[item.Key] += item.Value;
				}
			}
			return dictionary;
		}

		private static List<string> OrderedFamilies(Dictionary<string, int> counts)
		{
			List<string> list = new List<string>(counts.Keys);
			list.Sort((string a, string b) => counts[a].CompareTo(counts[b]));
			return list;
		}

		private PlantType ChooseType(string family)
		{
			foreach (PlantType tree in PlantCatalogue.Trees)
			{
				if (tree.Family == family && !_failedTypes.Contains(tree) && Supply(tree.RequiredShared) >= tree.Amount)
				{
					return tree;
				}
			}
			return null;
		}

		private List<Vector3> FindSpots(PlantType type, int max)
		{
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_049b: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_0447: Unknown result type (might be due to invalid IL or missing references)
			//IL_044c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0451: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_030e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0527: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_032d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0345: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			//IL_0356: Unknown result type (might be due to invalid IL or missing references)
			//IL_035b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0388: Unknown result type (might be due to invalid IL or missing references)
			//IL_038d: 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)
			int mask = LayerMask.GetMask(new string[5] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid" });
			int mask2 = LayerMask.GetMask(new string[3] { "Default", "static_solid", "piece" });
			int mask3 = LayerMask.GetMask(new string[2] { "piece", "piece_nonsolid" });
			int mask4 = LayerMask.GetMask(new string[1] { "terrain" });
			float value = WorkerConfig.JobRadius.Value;
			float num = Mathf.Max(type.Plant.m_growRadius, 0.5f);
			float value2 = WorkerConfig.TreeBuildingClearance.Value;
			int num2 = 0;
			List<Occupied> list = new List<Occupied>(_plantedSpots);
			Plant[] array = Object.FindObjectsByType<Plant>((FindObjectsSortMode)0);
			foreach (Plant val in array)
			{
				if ((Object)(object)val != (Object)null && Utils.DistanceXZ(((Component)val).transform.position, base.Home) <= value + 10f)
				{
					list.Add(new Occupied
					{
						Position = ((Component)val).transform.position,
						Radius = val.m_growRadius
					});
				}
			}
			TreeBase[] array2 = Object.FindObjectsByType<TreeBase>((FindObjectsSortMode)0);
			foreach (TreeBase val2 in array2)
			{
				if ((Object)(object)val2 != (Object)null && ((Component)val2).gameObject.activeInHierarchy && Utils.DistanceXZ(((Component)val2).transform.position, base.Home) <= value + 10f)
				{
					PlantType plantType = PlantCatalogue.Of(((Component)val2).gameObject);
					float radius = ((plantType != null && plantType.IsTree) ? plantType.Plant.m_growRadius : 2f);
					list.Add(new Occupied
					{
						Position = ((Component)val2).transform.position,
						Radius = radius
					});
				}
			}
			List<Vector3> list2 = new List<Vector3>();
			Vector3 val3 = default(Vector3);
			for (float num3 = 0f - value; num3 <= value; num3 += 2f)
			{
				for (float num4 = 0f - value; num4 <= value; num4 += 2f)
				{
					((Vector3)(ref val3))..ctor(base.Home.x + num3 + Random.Range(-0.6f, 0.6f), 0f, base.Home.z + num4 + Random.Range(-0.6f, 0.6f));
					if (!InJobArea(val3))
					{
						continue;
					}
					val3.y = ZoneSystem.instance.GetGroundHeight(val3);
					Heightmap val4 = Heightmap.FindHeightmap(val3);
					if ((Object)(object)val4 == (Object)null || val3.y <= ZoneSystem.instance.m_waterLevel + 0.5f)
					{
						continue;
					}
					if (!PlantCatalogue.BiomeAllows(type, val3))
					{
						num2++;
					}
					else
					{
						if ((type.Piece.m_vegetationGroundOnly && !HasDirt(val4, val3)) || NearFarmland(val3) || Physics.CheckSphere(val3, value2, mask3) || !FlatEnough(val3, mask4) || Physics.CheckSphere(val3 + Vector3.up * 0.2f, num + 0.3f, mask) || Physics.Raycast(val3 + Vector3.up * 0.2f, Vector3.up, 100f, mask2))
						{
							continue;
						}
						bool flag = false;
						foreach (Occupied item in list)
						{
							flag |= Utils.DistanceXZ(item.Position, val3) < Mathf.Max(num, item.Radius) + 1f;
						}
						if (!flag)
						{
							list2.Add(val3);
						}
					}
				}
			}
			if (list2.Count == 0 && num2 > 0 && _loggedBiome.Add(type))
			{
				DvergrWorkersPlugin.Log.LogInfo((object)("Tree farmer: " + ((Object)type.Prefab).name + " cannot be planted here, wrong biome (" + ((object)Heightmap.FindBiome(base.Home)/*cast due to .constrained prefix*/).ToString() + " is not in plant biomes " + ((object)Unsafe.As<Biome, Biome>(ref type.Plant.m_biome)/*cast due to .constrained prefix*/).ToString() + ")"));
			}
			Vector3 here = ((Component)this).transform.position;
			list2.Sort((Vector3 a, Vector3 b) => Vector3.Distance(a, here).CompareTo(Vector3.Distance(b, here)));
			List<Vector3> list3 = new List<Vector3>();
			foreach (Vector3 item2 in list2)
			{
				bool flag2 = false;
				foreach (Vector3 item3 in list3)
				{
					flag2 |= Utils.DistanceXZ(item3, item2) < num * 2f + 1f;
				}
				if (!flag2)
				{
					list3.Add(item2);
					if (list3.Count >= max)
					{
						break;
					}
				}
			}
			return list3;
		}

		private static bool HasDirt(Heightmap heightmap, Vector3 p)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			float vegetationMask = heightmap.GetVegetationMask(p);
			if ((int)heightmap.GetBiome(p, 0.02f, false) != 32)
			{
				return vegetationMask >= 0.25f;
			}
			return vegetationMask <= 0.1f;
		}

		private static bool FlatEnough(Vector3 p, int terrainMask)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			RaycastHit val = default(RaycastHit);
			if (Physics.Raycast(p + Vector3.up * 2f, Vector3.down, ref val, 6f, terrainMask))
			{
				return ((RaycastHit)(ref val)).normal.y >= 0.8f;
			}
			return false;
		}

		private static bool NearFarmland(Vector3 p)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			Vector3[] array = (Vector3[])(object)new Vector3[5]
			{
				Vector3.zero,
				Vector3.right * 2.5f,
				Vector3.left * 2.5f,
				Vector3.forward * 2.5f,
				Vector3.back * 2.5f
			};
			foreach (Vector3 val in array)
			{
				Heightmap val2 = Heightmap.FindHeightmap(p + val);
				if ((Object)(object)val2 != (Object)null && val2.IsCultivated(p + val))
				{
					return true;
				}
			}
			return false;
		}

		protected override bool BeginRound()
		{
			_planted = 0;
			_noSupply = false;
			_noSpots = false;
			_full = false;
			_failedTypes.Clear();
			_loggedBiome.Clear();
			_plantedByFamily.Clear();
			_plantedSpots.Clear();
			_batchSpots.Clear();
			_batchType = null;
			if (!PlantCatalogue.Trees.Any())
			{
				base.IdleNote = "No tree saplings found in the cultivator";
				return false;
			}
			if (!AnythingToPlant())
			{
				_plantedLastRound = 0;
				base.IdleNote = NothingToDoNote();
				return false;
			}
			base.Equipment.EquipTool();
			_phase = Phase.Plan;
			DvergrWorkersPlugin.Log.LogInfo((object)"Tree farmer round started");
			return true;
		}

		private bool AnythingToPlant()
		{
			int total;
			Dictionary<string, int> counts = CountFamilies(out total);
			if (total >= WorkerConfig.MaxTrees.Value)
			{
				_full = true;
				return CarryingSeeds();
			}
			foreach (string item in OrderedFamilies(counts))
			{
				PlantType plantType = ChooseType(item);
				if (plantType == null)
				{
					_noSupply = true;
					continue;
				}
				if (FindSpots(plantType, 1).Count > 0)
				{
					return true;
				}
				_noSpots = true;
			}
			return CarryingSeeds();
		}

		private string NothingToDoNote()
		{
			if (_full)
			{
				return "The forest is full";
			}
			if (_noSupply && !_noSpots)
			{
				return "Waiting for tree seeds in a chest";
			}
			if (_noSpots && !_noSupply)
			{
				return "No suitable ground to plant trees";
			}
			return null;
		}

		protected override bool RunTask(float dt)
		{
			return _phase switch
			{
				Phase.Plan => Plan(), 
				Phase.Fetch => Fetch(dt), 
				Phase.Walk => Walk(dt), 
				Phase.Plant => PlantNow(dt), 
				Phase.Return => Return(dt), 
				_ => false, 
			};
		}

		private bool Plan()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if (_batchSpots.Count > 0 && _batchType != null && base.Logistics.Carried.CountItems(_batchType.RequiredShared, -1, true) >= _batchType.Amount)
			{
				return BeginWalk(_batchSpots[0]);
			}
			_batchSpots.Clear();
			if (StartBatch())
			{
				return true;
			}
			if (CarryingSeeds())
			{
				return StartReturn();
			}
			if (_planted == 0)
			{
				base.IdleNote = NothingToDoNote();
			}
			EndRound(finished: true);
			return false;
		}

		private bool StartBatch()
		{
			int total;
			Dictionary<string, int> counts = CountFamilies(out total);
			int num = WorkerConfig.MaxTrees.Value - total;
			if (num <= 0)
			{
				_full = true;
				return false;
			}
			foreach (string item in OrderedFamilies(counts))
			{
				PlantType plantType = ChooseType(item);
				if (plantType == null)
				{
					_noSupply = true;
					continue;
				}
				List<Vector3> list = FindSpots(plantType, Math.Min(WorkerConfig.TreesPerTrip.Value, num));
				if (list.Count == 0)
				{
					_noSpots = true;
					continue;
				}
				int num2 = Math.Min(list.Count, Supply(plantType.RequiredShared) / plantType.Amount);
				if (num2 > 0)
				{
					_batchType = plantType;
					_batchSpots.Clear();
					_batchSpots.AddRange(list.GetRange(0, num2));
					int num3 = num2 * plantType.Amount - base.Logistics.Carried.CountItems(plantType.RequiredShared, -1, true);
					if (num3 <= 0)
					{
						_phase = Phase.Plan;
						return true;
					}
					string shared = plantType.RequiredShared;
					if (base.Logistics.BeginFetchAny((ItemData i) => i.m_shared.m_name == shared, num3))
					{
						_phase = Phase.Fetch;
						SetStatus("Fetching seeds to plant");
						return true;
					}
					DvergrWorkersPlugin.Log.LogInfo((object)("Tree farmer fetch failed: " + base.Logistics.Reason));
					_failedTypes.Add(plantType);
					_batchSpots.Clear();
				}
			}
			return false;
		}

		private bool Fetch(float dt)
		{
			switch (base.Logistics.Step(dt))
			{
			case LogisticsResult.Moving:
				return true;
			case LogisticsResult.Failed:
				DvergrWorkersPlugin.Log.LogInfo((object)("Tree farmer fetch failed: " + base.Logistics.Reason));
				if (_batchType != null)
				{
					_failedTypes