Decompiled source of FulingWorkforce v1.0.0

plugins\FulingWorkforce.dll

Decompiled 21 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace FulingWorkforce
{
	public sealed class BartenderPostMarker : MonoBehaviour, TextReceiver
	{
		private const string NameKey = "fw_bartender_post_name";

		private const string StatusKey = "fw_bartender_status";

		private const string TransfersKey = "fw_bartender_transfers";

		private static readonly HashSet<BartenderPostMarker> Active = new HashSet<BartenderPostMarker>();

		private int _selectedSlot;

		private int _fermenterCursor;

		private bool _editName;

		private bool _queuedEdit;

		private float _lastInteraction;

		private float _nextWork;

		private object _textUser;

		internal string PostName => R.ZString(((Component)this).gameObject, "fw_bartender_post_name");

		internal string CurrentStatus => R.ZString(((Component)this).gameObject, "fw_bartender_status", "Inactive — no Bartender assigned through a Bartender Bunk");

		private void OnEnable()
		{
			Active.Add(this);
		}

		private void OnDisable()
		{
			Active.Remove(this);
		}

		private void Start()
		{
			_nextWork = Time.time + Random.Range(0.5f, 1.5f);
		}

		private void LateUpdate()
		{
			if (!_queuedEdit)
			{
				return;
			}
			_queuedEdit = false;
			try
			{
				string text = (_editName ? "Bartender post name" : (SlotLabel(_selectedSlot) + " chest name"));
				if ((Object)(object)TextInput.instance != (Object)null)
				{
					TextInput.instance.RequestText((TextReceiver)(object)this, text, 32);
				}
				else
				{
					Plugin.Message(_textUser, "The text-entry window is unavailable.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not open Bartender Post editor: " + ex.Message));
				Plugin.Message(_textUser, "The Bartender Post editor could not be opened.");
			}
		}

		private void Update()
		{
			if (Time.time < _nextWork)
			{
				return;
			}
			_nextWork = Time.time + Mathf.Max(0.5f, Plugin.BartenderWorkInterval.Value);
			if (!R.IsOwner(((Component)this).gameObject))
			{
				return;
			}
			if ((Object)(object)JobStationMarker.FindBartenderBunkFor(this, out var worker) == (Object)null || (Object)(object)worker == (Object)null)
			{
				SetStatus("Inactive — no Bartender assigned through a Bartender Bunk");
				return;
			}
			string problem;
			NamedContainerMarker namedContainerMarker = Storage(0, out problem);
			if ((Object)(object)namedContainerMarker == (Object)null)
			{
				SetStatus("Paused — mead base chest " + problem);
				return;
			}
			string problem2;
			NamedContainerMarker namedContainerMarker2 = Storage(1, out problem2);
			if ((Object)(object)namedContainerMarker2 == (Object)null)
			{
				SetStatus("Paused — finished mead chest " + problem2);
				return;
			}
			List<Component> fermenters = FindFermenters();
			string action2;
			string waiting;
			string action3;
			string action4;
			if (TryCollectOutput(fermenters, namedContainerMarker2, out var action))
			{
				RecordTransfer(action);
			}
			else if (BartenderServingFrame.TryStockNext(this, namedContainerMarker2, out action2, out waiting))
			{
				RecordTransfer(action2);
			}
			else if (TryTapReady(fermenters, namedContainerMarker2, out action3))
			{
				RecordTransfer(action3);
			}
			else if (TryLoadEmpty(fermenters, namedContainerMarker, out action4))
			{
				RecordTransfer(action4);
			}
			else
			{
				SetIdleStatus(fermenters, namedContainerMarker, namedContainerMarker2, waiting);
			}
		}

		internal bool Interact(object user, bool hold, bool alt, bool control)
		{
			if (Time.time - _lastInteraction < 0.45f)
			{
				return true;
			}
			_lastInteraction = Time.time;
			_textUser = user;
			if (control && !alt)
			{
				_selectedSlot = (_selectedSlot + 1) % 2;
				Plugin.Message(user, "Selected " + SlotLabel(_selectedSlot) + " chest.");
				return true;
			}
			if (hold)
			{
				return true;
			}
			_editName = !alt;
			_queuedEdit = true;
			return true;
		}

		public string GetText()
		{
			if (!_editName)
			{
				return R.ZString(((Component)this).gameObject, SlotKey(_selectedSlot, "name"));
			}
			return PostName;
		}

		public void SetText(string text)
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			string text2 = PatrolPointMarker.CleanLabel(text);
			R.ClaimOwnership(((Component)this).gameObject);
			if (_editName)
			{
				R.ZSet(((Component)this).gameObject, "fw_bartender_post_name", text2);
				R.Call(R.Component(((Component)this).gameObject, "Sign"), "SetText", text2);
				Plugin.Message(_textUser, string.IsNullOrEmpty(text2) ? "Bartender Post name cleared." : ("Bartender Post named " + text2 + "."));
				return;
			}
			string key = SlotKey(_selectedSlot, "name");
			string key2 = SlotKey(_selectedSlot, "pos");
			string key3 = SlotKey(_selectedSlot, "linked");
			R.ZSet(((Component)this).gameObject, key, text2);
			bool ambiguous;
			NamedContainerMarker namedContainerMarker = NamedContainerMarker.FindNamed(text2, ((Component)this).transform.position, Plugin.BartenderStorageRange.Value, out ambiguous);
			if ((Object)(object)namedContainerMarker != (Object)null)
			{
				R.ZSet(((Component)this).gameObject, key2, ((Component)namedContainerMarker).transform.position);
				R.ZSet(((Component)this).gameObject, key3, true);
				Plugin.Message(_textUser, SlotLabel(_selectedSlot) + " chest linked to " + namedContainerMarker.StorageName + ".");
			}
			else
			{
				R.ZSet(((Component)this).gameObject, key3, false);
				string text3 = (ambiguous ? "More than one nearby chest has that storage name." : (string.IsNullOrEmpty(text2) ? (SlotLabel(_selectedSlot) + " chest link cleared.") : $"No storage named '{text2}' is within {Plugin.BartenderStorageRange.Value:0}m."));
				Plugin.Message(_textUser, text3);
			}
		}

		internal string HoverText()
		{
			string text = (string.IsNullOrEmpty(PostName) ? "<color=#aaaaaa>Not named</color>" : ("<color=#7CFF7C>" + PostName + "</color>"));
			string text2 = "";
			for (int i = 0; i < 2; i++)
			{
				string text3 = ((i == _selectedSlot) ? "<color=#FFD966>▶</color> " : "  ");
				text2 = text2 + text3 + SlotLabel(i) + ": " + StorageDisplay(i) + "\n";
			}
			int num = BartenderServingFrame.CountFor(this);
			return "Fuling Bartender Post\nName: " + text + "\n" + text2 + string.Format("Serving frames: {0}\nStatus: {1}\nTransfers: {2}\n", num, CurrentStatus, R.ZInt(((Component)this).gameObject, "fw_bartender_transfers")) + "[<color=yellow><b>E</b></color>] Edit post name\n[<color=yellow><b>Ctrl + E</b></color>] Select chest\n[<color=yellow><b>Shift + E</b></color>] Set selected chest";
		}

		internal static BartenderPostMarker FindNamed(string name, Vector3 origin, float radius, out bool ambiguous)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			ambiguous = false;
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			BartenderPostMarker bartenderPostMarker = null;
			float num = radius * radius;
			foreach (BartenderPostMarker item in Active)
			{
				if (!Usable(item) || !string.Equals(item.PostName, name, StringComparison.OrdinalIgnoreCase))
				{
					continue;
				}
				Vector3 val = ((Component)item).transform.position - origin;
				if (!(((Vector3)(ref val)).sqrMagnitude > num))
				{
					if ((Object)(object)bartenderPostMarker != (Object)null && (Object)(object)bartenderPostMarker != (Object)(object)item)
					{
						ambiguous = true;
						return null;
					}
					bartenderPostMarker = item;
				}
			}
			return bartenderPostMarker;
		}

		internal static BartenderPostMarker FindAtPosition(Vector3 position)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			BartenderPostMarker result = null;
			float num = 2.25f;
			foreach (BartenderPostMarker item in Active)
			{
				if (Usable(item))
				{
					Vector3 val = ((Component)item).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (!(sqrMagnitude >= num))
					{
						num = sqrMagnitude;
						result = item;
					}
				}
			}
			return result;
		}

		internal static BartenderPostMarker FindNearest(Vector3 position, float radius)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			BartenderPostMarker result = null;
			float num = radius * radius;
			foreach (BartenderPostMarker item in Active)
			{
				if (Usable(item))
				{
					Vector3 val = ((Component)item).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (!(sqrMagnitude >= num))
					{
						num = sqrMagnitude;
						result = item;
					}
				}
			}
			return result;
		}

		internal bool Matches(string name, Vector3 position)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (!string.IsNullOrEmpty(name) && string.Equals(PostName, name, StringComparison.OrdinalIgnoreCase))
			{
				return Vector3.Distance(((Component)this).transform.position, position) <= 2f;
			}
			Vector3 val = ((Component)this).transform.position - position;
			return ((Vector3)(ref val)).sqrMagnitude <= 2.25f;
		}

		internal bool IsFermenterProduct(int itemHash)
		{
			foreach (Component item in FindFermenters())
			{
				foreach (object item2 in Conversions(item))
				{
					GameObject val = AsGameObject(R.Get(item2, "m_to"));
					if ((Object)(object)val != (Object)null && R.StableHash(R.CleanName(val)) == itemHash)
					{
						return true;
					}
				}
			}
			return false;
		}

		private NamedContainerMarker Storage(int slot, out string problem)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			string text = R.ZString(((Component)this).gameObject, SlotKey(slot, "name"));
			if (string.IsNullOrEmpty(text))
			{
				problem = "is not configured";
				return null;
			}
			bool flag = R.ZBool(((Component)this).gameObject, SlotKey(slot, "linked"));
			Vector3 val = R.ZVector(((Component)this).gameObject, SlotKey(slot, "pos"), ((Component)this).transform.position);
			NamedContainerMarker namedContainerMarker = (flag ? NamedContainerMarker.FindAtPosition(val) : null);
			bool ambiguous = false;
			if ((Object)(object)namedContainerMarker == (Object)null)
			{
				namedContainerMarker = NamedContainerMarker.FindNamed(text, ((Component)this).transform.position, Plugin.BartenderStorageRange.Value, out ambiguous);
			}
			if (ambiguous)
			{
				problem = "name is ambiguous";
				return null;
			}
			if ((Object)(object)namedContainerMarker == (Object)null)
			{
				problem = $"was not found within {Plugin.BartenderStorageRange.Value:0}m";
				return null;
			}
			if (Vector3.Distance(((Component)this).transform.position, ((Component)namedContainerMarker).transform.position) > Plugin.BartenderStorageRange.Value)
			{
				problem = $"is more than {Plugin.BartenderStorageRange.Value:0}m away";
				return null;
			}
			if (R.IsOwner(((Component)this).gameObject))
			{
				if (flag && string.Equals(text, namedContainerMarker.StorageName, StringComparison.Ordinal))
				{
					Vector3 val2 = val - ((Component)namedContainerMarker).transform.position;
					if (!(((Vector3)(ref val2)).sqrMagnitude > 0.01f))
					{
						goto IL_01b6;
					}
				}
				R.ZSet(((Component)this).gameObject, SlotKey(slot, "name"), namedContainerMarker.StorageName);
				R.ZSet(((Component)this).gameObject, SlotKey(slot, "pos"), ((Component)namedContainerMarker).transform.position);
				R.ZSet(((Component)this).gameObject, SlotKey(slot, "linked"), true);
			}
			goto IL_01b6;
			IL_01b6:
			problem = null;
			return namedContainerMarker;
		}

		private string StorageDisplay(int slot)
		{
			string text = R.ZString(((Component)this).gameObject, SlotKey(slot, "name"));
			string problem;
			NamedContainerMarker namedContainerMarker = Storage(slot, out problem);
			if ((Object)(object)namedContainerMarker != (Object)null)
			{
				return "<color=#7CFF7C>" + namedContainerMarker.StorageName + "</color>";
			}
			if (string.IsNullOrEmpty(text))
			{
				return "<color=#aaaaaa>Not configured</color>";
			}
			return "<color=#FF7777>" + text + " — " + problem + "</color>";
		}

		private List<Component> FindFermenters()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			List<Component> list = new List<Component>();
			HashSet<int> hashSet = new HashSet<int>();
			foreach (Component item in R.FindNear("Fermenter", ((Component)this).transform.position, Plugin.BartenderRadius.Value))
			{
				if (!((Object)(object)item == (Object)null) && item.gameObject.activeInHierarchy && hashSet.Add(((Object)item).GetInstanceID()))
				{
					list.Add(item);
				}
			}
			return list;
		}

		private bool TryCollectOutput(List<Component> fermenters, NamedContainerMarker output, out string action)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			action = null;
			object inventory = output.Inventory;
			if (inventory == null)
			{
				return false;
			}
			for (int i = 0; i < fermenters.Count; i++)
			{
				Component fermenter = fermenters[(_fermenterCursor + i) % fermenters.Count];
				foreach (Component item in R.FindNear("ItemDrop", OutputPosition(fermenter), 4f))
				{
					if ((Object)(object)item == (Object)null || !IsOutput(fermenter, item.gameObject))
					{
						continue;
					}
					object obj = R.Call(R.Get(item, "m_itemData"), "Clone");
					if (obj != null && CanAdd(inventory, obj))
					{
						R.ClaimOwnership(((Component)output).gameObject);
						object obj2 = R.Call(inventory, "AddItem", obj);
						if (obj2 is bool && (bool)obj2)
						{
							R.ClaimOwnership(item.gameObject);
							R.DestroyNetworked(item.gameObject);
							_fermenterCursor = (_fermenterCursor + i + 1) % fermenters.Count;
							action = "Active — collected " + ItemDisplayName(obj);
							return true;
						}
					}
				}
			}
			return false;
		}

		private bool TryTapReady(List<Component> fermenters, NamedContainerMarker output, out string action)
		{
			action = null;
			object inventory = output.Inventory;
			for (int i = 0; i < fermenters.Count; i++)
			{
				int num = (_fermenterCursor + i) % fermenters.Count;
				Component val = fermenters[num];
				if (Number(R.Call(val, "GetStatus")) != 3)
				{
					continue;
				}
				object obj = CurrentConversion(val);
				if (obj != null)
				{
					object obj2 = R.Call(ItemData(AsGameObject(R.Get(obj, "m_to"))), "Clone");
					if (obj2 != null)
					{
						int num2 = Math.Max(1, Number(R.Get(obj, "m_producedItems")));
						R.Set(obj2, "m_stack", num2);
						if (!CanAdd(inventory, obj2))
						{
							continue;
						}
					}
				}
				if (InvokeRpc(val, "RPC_Tap"))
				{
					_fermenterCursor = (num + 1) % fermenters.Count;
					action = "Active — tapped a finished fermenter";
					return true;
				}
			}
			return false;
		}

		private bool TryLoadEmpty(List<Component> fermenters, NamedContainerMarker input, out string action)
		{
			action = null;
			object inventory = input.Inventory;
			if (inventory == null)
			{
				return false;
			}
			bool flag = default(bool);
			for (int i = 0; i < fermenters.Count; i++)
			{
				int num = (_fermenterCursor + i) % fermenters.Count;
				Component val = fermenters[num];
				if (Number(R.Call(val, "GetStatus")) != 0 || IsExposed(val))
				{
					continue;
				}
				object obj = FindAcceptedInput(inventory, val);
				if (obj != null)
				{
					object obj2 = R.Get(obj, "m_dropPrefab");
					int num2 = R.StableHash(R.CleanName((GameObject)((obj2 is GameObject) ? obj2 : null)));
					object obj3 = R.Get(obj, "m_cheated");
					int num3;
					if (obj3 is bool)
					{
						flag = (bool)obj3;
						num3 = 1;
					}
					else
					{
						num3 = 0;
					}
					bool flag2 = (byte)((uint)num3 & (flag ? 1u : 0u)) != 0;
					if (num2 != 0 && InvokeRpc(val, "RPC_AddItem", num2, flag2))
					{
						RemoveOne(input, inventory, obj);
						_fermenterCursor = (num + 1) % fermenters.Count;
						action = "Active — loaded " + ItemDisplayName(obj) + " into a fermenter";
						return true;
					}
				}
			}
			return false;
		}

		private void SetIdleStatus(List<Component> fermenters, NamedContainerMarker input, NamedContainerMarker output, string frameWaiting)
		{
			if (fermenters.Count == 0)
			{
				SetStatus($"Active — no fermenters within {Plugin.BartenderRadius.Value:0}m");
				return;
			}
			bool flag = false;
			bool flag2 = false;
			bool flag3 = false;
			bool flag4 = false;
			foreach (Component fermenter in fermenters)
			{
				switch (Number(R.Call(fermenter, "GetStatus")))
				{
				case 3:
					flag2 = true;
					break;
				case 0:
					flag4 = true;
					if (IsExposed(fermenter))
					{
						flag3 = true;
					}
					break;
				default:
					flag = true;
					break;
				}
			}
			if (flag2 && !CanAcceptReady(fermenters, output.Inventory))
			{
				SetStatus("Paused — finished mead chest is full");
			}
			else if (!string.IsNullOrEmpty(frameWaiting))
			{
				SetStatus(frameWaiting);
			}
			else if (flag || flag2)
			{
				SetStatus("Active — fermenters are working or ready");
			}
			else if (flag4 && !HasAcceptedInput(input.Inventory, fermenters))
			{
				SetStatus("Paused — mead base chest needs a compatible mead base");
			}
			else if (flag3)
			{
				SetStatus("Paused — empty fermenters need shelter");
			}
			else
			{
				SetStatus("Active — fermenters are supplied");
			}
		}

		private static bool CanAcceptReady(List<Component> fermenters, object inventory)
		{
			foreach (Component fermenter in fermenters)
			{
				if (Number(R.Call(fermenter, "GetStatus")) == 3)
				{
					object obj = R.Call(ItemData(AsGameObject(R.Get(CurrentConversion(fermenter), "m_to"))), "Clone");
					if (obj == null || CanAdd(inventory, obj))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool HasAcceptedInput(object inventory, List<Component> fermenters)
		{
			foreach (Component fermenter in fermenters)
			{
				if (FindAcceptedInput(inventory, fermenter) != null)
				{
					return true;
				}
			}
			return false;
		}

		private static object FindAcceptedInput(object inventory, Component fermenter)
		{
			foreach (object item in InventoryItems(inventory))
			{
				object obj = R.Get(item, "m_dropPrefab");
				int num = R.StableHash(R.CleanName((GameObject)((obj is GameObject) ? obj : null)));
				foreach (object item2 in Conversions(fermenter))
				{
					GameObject val = AsGameObject(R.Get(item2, "m_from"));
					if ((Object)(object)val != (Object)null && R.StableHash(R.CleanName(val)) == num)
					{
						return item;
					}
				}
			}
			return null;
		}

		private static object CurrentConversion(Component fermenter)
		{
			int num = Number(R.Call(fermenter, "GetContent"));
			foreach (object item in Conversions(fermenter))
			{
				GameObject val = AsGameObject(R.Get(item, "m_from"));
				if ((Object)(object)val != (Object)null && R.StableHash(R.CleanName(val)) == num)
				{
					return item;
				}
			}
			return null;
		}

		private static bool IsOutput(Component fermenter, GameObject dropObject)
		{
			object obj = R.Get(R.Get(R.Component(dropObject, "ItemDrop"), "m_itemData"), "m_dropPrefab");
			int num = R.StableHash(R.CleanName((GameObject)(((obj is GameObject) ? obj : null) ?? dropObject)));
			foreach (object item in Conversions(fermenter))
			{
				GameObject val = AsGameObject(R.Get(item, "m_to"));
				if ((Object)(object)val != (Object)null && R.StableHash(R.CleanName(val)) == num)
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsExposed(Component fermenter)
		{
			object obj = R.Get(fermenter, "m_exposed");
			bool flag = default(bool);
			int num;
			if (obj is bool)
			{
				flag = (bool)obj;
				num = 1;
			}
			else
			{
				num = 0;
			}
			if (((uint)num & (flag ? 1u : 0u)) != 0)
			{
				return true;
			}
			if (R.Get(fermenter, "m_hasRoof") is bool flag2)
			{
				return !flag2;
			}
			return false;
		}

		private static Vector3 OutputPosition(Component fermenter)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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)
			object obj = R.Get(fermenter, "m_outputPoint");
			object obj2 = ((obj is Transform) ? obj : null);
			if (obj2 == null)
			{
				object obj3 = ((obj is Component) ? obj : null);
				obj2 = ((obj3 != null) ? ((Component)obj3).transform : null);
				if (obj2 == null)
				{
					object obj4 = ((obj is GameObject) ? obj : null);
					obj2 = ((obj4 != null) ? ((GameObject)obj4).transform : null);
				}
			}
			Transform val = (Transform)obj2;
			if (!((Object)(object)val != (Object)null))
			{
				return fermenter.transform.position + fermenter.transform.forward * 2f;
			}
			return val.position;
		}

		private static bool InvokeRpc(Component component, string method, params object[] arguments)
		{
			object obj = R.Get(component, "m_nview");
			ZNetView val = (ZNetView)(((obj is ZNetView) ? obj : null) ?? component.GetComponent<ZNetView>() ?? component.GetComponentInParent<ZNetView>());
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return false;
			}
			try
			{
				val.InvokeRPC(method, arguments ?? new object[0]);
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not send " + method + " to fermenter: " + ex.Message));
				return false;
			}
		}

		private static void RemoveOne(NamedContainerMarker storage, object inventory, object item)
		{
			R.ClaimOwnership(((Component)storage).gameObject);
			string text = ItemSharedName(item);
			int num = Number(R.Call(inventory, "CountItems", text));
			R.Call(inventory, "RemoveOneItem", item);
			if (num > 0 && Number(R.Call(inventory, "CountItems", text)) >= num)
			{
				R.Call(inventory, "RemoveItem", text, 1);
			}
		}

		internal static IEnumerable<object> InventoryItems(object inventory)
		{
			if (!(R.Call(inventory, "GetAllItems") is IEnumerable enumerable))
			{
				yield break;
			}
			foreach (object item in enumerable)
			{
				if (item != null && Number(R.Get(item, "m_stack")) > 0)
				{
					yield return item;
				}
			}
		}

		private static IEnumerable<object> Conversions(Component fermenter)
		{
			if (!(R.Get(fermenter, "m_conversion") is IEnumerable enumerable))
			{
				yield break;
			}
			foreach (object item in enumerable)
			{
				if (item != null)
				{
					yield return item;
				}
			}
		}

		internal static bool CanAdd(object inventory, object itemData)
		{
			if (inventory == null || itemData == null)
			{
				return false;
			}
			object obj = R.Call(inventory, "CanAddItem", itemData);
			if (obj is bool)
			{
				return (bool)obj;
			}
			object obj2 = R.Call(inventory, "HaveEmptySlot");
			bool flag = default(bool);
			int num;
			if (obj2 is bool)
			{
				flag = (bool)obj2;
				num = ((1 == 0) ? 1 : 0);
			}
			else
			{
				num = 1;
			}
			return (byte)((uint)num | (flag ? 1u : 0u)) != 0;
		}

		private static object ItemData(GameObject prefab)
		{
			return R.Get(R.Component(prefab, "ItemDrop"), "m_itemData");
		}

		private static GameObject AsGameObject(object value)
		{
			object obj = ((value is GameObject) ? value : null);
			if (obj == null)
			{
				object obj2 = ((value is Component) ? value : null);
				if (obj2 == null)
				{
					return null;
				}
				obj = ((Component)obj2).gameObject;
			}
			return (GameObject)obj;
		}

		internal static string ItemSharedName(object itemData)
		{
			return (R.Get(R.Get(itemData, "m_shared"), "m_name") as string) ?? ItemDisplayName(itemData);
		}

		internal static string ItemDisplayName(object itemData)
		{
			string text = R.Get(R.Get(itemData, "m_shared"), "m_name") as string;
			if (!string.IsNullOrEmpty(text))
			{
				return text.Replace("$item_", "").Replace("_", " ");
			}
			object obj = R.Get(itemData, "m_dropPrefab");
			return R.CleanName((GameObject)((obj is GameObject) ? obj : null));
		}

		internal static int Number(object value)
		{
			try
			{
				return (value != null) ? Convert.ToInt32(value) : 0;
			}
			catch
			{
				return 0;
			}
		}

		private static string SlotLabel(int slot)
		{
			if (slot != 0)
			{
				return "Finished mead";
			}
			return "Mead base";
		}

		private static string SlotKey(int slot, string suffix)
		{
			return $"fw_bartender_slot_{slot}_{suffix}";
		}

		private void RecordTransfer(string status)
		{
			R.ZSet(((Component)this).gameObject, "fw_bartender_transfers", R.ZInt(((Component)this).gameObject, "fw_bartender_transfers") + 1);
			SetStatus(status);
		}

		private void SetStatus(string status)
		{
			if (!string.Equals(R.ZString(((Component)this).gameObject, "fw_bartender_status"), status, StringComparison.Ordinal))
			{
				R.ZSet(((Component)this).gameObject, "fw_bartender_status", status);
			}
		}

		private static bool Usable(BartenderPostMarker post)
		{
			if ((Object)(object)post != (Object)null && ((Component)post).gameObject.activeInHierarchy)
			{
				return R.Zdo(((Component)post).gameObject) != null;
			}
			return false;
		}
	}
	public sealed class BartenderServingFrame : MonoBehaviour
	{
		private const string LinkedKey = "fw_bartender_frame_linked";

		private const string PostNameKey = "fw_bartender_frame_post_name";

		private const string PostPositionKey = "fw_bartender_frame_post_pos";

		private const string ItemHashKey = "fw_bartender_frame_item_hash";

		private const string ItemNameKey = "fw_bartender_frame_item_name";

		private static readonly HashSet<BartenderServingFrame> Active = new HashSet<BartenderServingFrame>();

		private float _lastInteraction;

		private void OnEnable()
		{
			Active.Add(this);
		}

		private void OnDisable()
		{
			Active.Remove(this);
		}

		internal static BartenderServingFrame Ensure(Component stand)
		{
			if ((Object)(object)stand == (Object)null || !Supported(stand.gameObject))
			{
				return null;
			}
			return stand.GetComponent<BartenderServingFrame>() ?? stand.GetComponentInParent<BartenderServingFrame>() ?? stand.gameObject.AddComponent<BartenderServingFrame>();
		}

		internal bool Interact(object user, bool hold, bool alt, bool control)
		{
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			if (Time.time - _lastInteraction < 0.35f)
			{
				return true;
			}
			_lastInteraction = Time.time;
			if (hold)
			{
				return true;
			}
			if (control && !alt)
			{
				R.ClaimOwnership(((Component)this).gameObject);
				R.ZSet(((Component)this).gameObject, "fw_bartender_frame_linked", false);
				R.ZSet(((Component)this).gameObject, "fw_bartender_frame_post_name", "");
				R.ZSet(((Component)this).gameObject, "fw_bartender_frame_item_hash", 0);
				R.ZSet(((Component)this).gameObject, "fw_bartender_frame_item_name", "");
				Plugin.Message(user, "Bartender serving-frame link cleared. Its displayed item was left in place.");
				return true;
			}
			if (!alt)
			{
				return false;
			}
			Component instance = R.Component(((Component)this).gameObject, "ItemStand");
			object obj = R.Call(instance, "HaveAttachment");
			if (!(obj is bool) || !(bool)obj)
			{
				Plugin.Message(user, "Place the finished mead you want on this item frame first.");
				return true;
			}
			int num = Number(R.Call(instance, "GetAttachedItem"));
			if (num == 0)
			{
				Plugin.Message(user, "The displayed item could not be identified.");
				return true;
			}
			BartenderPostMarker bartenderPostMarker = BartenderPostMarker.FindNearest(((Component)this).transform.position, Plugin.BartenderFrameRange.Value);
			if ((Object)(object)bartenderPostMarker == (Object)null)
			{
				Plugin.Message(user, $"No Bartender Post is within {Plugin.BartenderFrameRange.Value:0}m.");
				return true;
			}
			if (!bartenderPostMarker.IsFermenterProduct(num))
			{
				Plugin.Message(user, "That item is not a product of a fermenter managed by the nearby Bartender Post.");
				return true;
			}
			string text = ProductName(bartenderPostMarker, num);
			R.ClaimOwnership(((Component)this).gameObject);
			R.ZSet(((Component)this).gameObject, "fw_bartender_frame_linked", true);
			R.ZSet(((Component)this).gameObject, "fw_bartender_frame_post_name", bartenderPostMarker.PostName);
			R.ZSet(((Component)this).gameObject, "fw_bartender_frame_post_pos", ((Component)bartenderPostMarker).transform.position);
			R.ZSet(((Component)this).gameObject, "fw_bartender_frame_item_hash", num);
			R.ZSet(((Component)this).gameObject, "fw_bartender_frame_item_name", text);
			Plugin.Message(user, "Serving frame registered to " + bartenderPostMarker.PostName + " for " + text + ".");
			return true;
		}

		internal string HoverText(string original)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			bool flag = R.ZBool(((Component)this).gameObject, "fw_bartender_frame_linked");
			BartenderPostMarker bartenderPostMarker = (flag ? LinkedPost() : BartenderPostMarker.FindNearest(((Component)this).transform.position, Plugin.BartenderFrameRange.Value));
			if (!flag && (Object)(object)bartenderPostMarker == (Object)null)
			{
				return original;
			}
			string text;
			if (!flag)
			{
				text = "\n[<color=yellow><b>Shift + E</b></color>] Register displayed mead with nearest Bartender Post";
			}
			else
			{
				object obj = R.Call(R.Component(((Component)this).gameObject, "ItemStand"), "HaveAttachment");
				bool flag2 = default(bool);
				int num;
				if (obj is bool)
				{
					flag2 = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				bool flag3 = (byte)((uint)num & (flag2 ? 1u : 0u)) != 0;
				string text2 = (((Object)(object)bartenderPostMarker == (Object)null) ? "<color=#FF7777>Missing</color>" : ("<color=#7CFF7C>" + bartenderPostMarker.PostName + "</color>"));
				string text3 = R.ZString(((Component)this).gameObject, "fw_bartender_frame_item_name", "Unknown mead");
				text = "\nBartender: " + text2 + "\nRequested stock: " + text3 + "\n" + (flag3 ? "Status: Stocked" : "Status: Waiting for finished mead") + "\n[<color=yellow><b>Shift + E</b></color>] Register current displayed mead\n[<color=yellow><b>Ctrl + E</b></color>] Clear Bartender link";
			}
			return (original ?? "Item Stand") + text;
		}

		internal static int CountFor(BartenderPostMarker post)
		{
			Discover(post);
			int num = 0;
			foreach (BartenderServingFrame item in Active)
			{
				if (Usable(item) && (Object)(object)item.LinkedPost() == (Object)(object)post)
				{
					num++;
				}
			}
			return num;
		}

		internal static bool TryStockNext(BartenderPostMarker post, NamedContainerMarker output, out string action, out string waiting)
		{
			action = null;
			waiting = null;
			Discover(post);
			object inventory = output.Inventory;
			bool flag = default(bool);
			foreach (BartenderServingFrame item in Active)
			{
				if (!Usable(item) || (Object)(object)item.LinkedPost() != (Object)(object)post)
				{
					continue;
				}
				Component val = R.Component(((Component)item).gameObject, "ItemStand");
				object obj = R.Call(val, "HaveAttachment");
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) == 0)
				{
					int hash = R.ZInt(((Component)item).gameObject, "fw_bartender_frame_item_hash");
					object obj2 = FindItem(inventory, hash);
					if (obj2 == null)
					{
						waiting = "Active — serving frame is waiting for " + R.ZString(((Component)item).gameObject, "fw_bartender_frame_item_name", "finished mead");
					}
					else if (item.Attach(val, obj2))
					{
						RemoveOne(output, inventory, obj2);
						action = "Active — stocked " + BartenderPostMarker.ItemDisplayName(obj2) + " on a serving frame";
						return true;
					}
				}
			}
			return false;
		}

		private bool Attach(Component stand, object itemData)
		{
			try
			{
				object obj = R.Call(itemData, "Clone");
				if (obj == null)
				{
					return false;
				}
				R.Set(obj, "m_stack", 1);
				object obj2 = R.Get(obj, "m_dropPrefab");
				int num = R.StableHash(R.CleanName((GameObject)((obj2 is GameObject) ? obj2 : null)));
				if (num == 0)
				{
					return false;
				}
				R.ClaimOwnership(((Component)this).gameObject);
				object obj3 = R.Zdo(((Component)this).gameObject);
				if (obj3 == null)
				{
					return false;
				}
				R.ZSet(((Component)this).gameObject, "item", num);
				R.CallStatic(R.Type("ItemDrop"), "SaveToZDO", obj, obj3, -1);
				int num2 = Number(R.Get(obj, "m_variant"));
				int num3 = Math.Max(1, Number(R.Get(obj, "m_quality")));
				int num4 = Number(R.Call(stand, "GetOrientation"));
				ZNetView val = stand.GetComponent<ZNetView>() ?? stand.GetComponentInParent<ZNetView>();
				if ((Object)(object)val == (Object)null || !val.IsValid())
				{
					return false;
				}
				val.InvokeRPC(ZNetView.Everybody, "SetVisualItem", new object[4] { num, num2, num3, num4 });
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not stock Bartender serving frame: " + ex.Message));
				return false;
			}
		}

		private BartenderPostMarker LinkedPost()
		{
			//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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			if (!R.ZBool(((Component)this).gameObject, "fw_bartender_frame_linked"))
			{
				return null;
			}
			string name = R.ZString(((Component)this).gameObject, "fw_bartender_frame_post_name");
			Vector3 position = R.ZVector(((Component)this).gameObject, "fw_bartender_frame_post_pos", ((Component)this).transform.position);
			BartenderPostMarker bartenderPostMarker = BartenderPostMarker.FindAtPosition(position);
			if ((Object)(object)bartenderPostMarker != (Object)null && bartenderPostMarker.Matches(name, position))
			{
				return bartenderPostMarker;
			}
			bartenderPostMarker = BartenderPostMarker.FindNamed(name, ((Component)this).transform.position, Plugin.BartenderFrameRange.Value, out var ambiguous);
			if (ambiguous || (Object)(object)bartenderPostMarker == (Object)null)
			{
				return null;
			}
			if (R.IsOwner(((Component)this).gameObject))
			{
				R.ZSet(((Component)this).gameObject, "fw_bartender_frame_post_name", bartenderPostMarker.PostName);
				R.ZSet(((Component)this).gameObject, "fw_bartender_frame_post_pos", ((Component)bartenderPostMarker).transform.position);
			}
			return bartenderPostMarker;
		}

		private static void Discover(BartenderPostMarker post)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			foreach (Component item in R.FindNear("ItemStand", ((Component)post).transform.position, Plugin.BartenderFrameRange.Value))
			{
				Ensure(item);
			}
		}

		private static object FindItem(object inventory, int hash)
		{
			foreach (object item in BartenderPostMarker.InventoryItems(inventory))
			{
				object obj = R.Get(item, "m_dropPrefab");
				if (R.StableHash(R.CleanName((GameObject)((obj is GameObject) ? obj : null))) == hash)
				{
					return item;
				}
			}
			return null;
		}

		private static string ProductName(BartenderPostMarker post, int hash)
		{
			object obj = R.Call(R.StaticProperty(R.Type("ZNetScene"), "instance") ?? R.StaticProperty(R.Type("ZNetScene"), "m_instance") ?? R.StaticProperty(R.Type("ZNetScene"), "s_instance"), "GetPrefab", hash);
			string text = BartenderPostMarker.ItemDisplayName(R.Get(R.Component((GameObject)((obj is GameObject) ? obj : null), "ItemDrop"), "m_itemData"));
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return "finished mead";
		}

		private static void RemoveOne(NamedContainerMarker storage, object inventory, object item)
		{
			R.ClaimOwnership(((Component)storage).gameObject);
			string text = BartenderPostMarker.ItemSharedName(item);
			int num = Number(R.Call(inventory, "CountItems", text));
			R.Call(inventory, "RemoveOneItem", item);
			if (num > 0 && Number(R.Call(inventory, "CountItems", text)) >= num)
			{
				R.Call(inventory, "RemoveItem", text, 1);
			}
		}

		private static bool Supported(GameObject go)
		{
			string text = R.CleanName(go).ToLowerInvariant();
			if (!(text == "itemstand"))
			{
				return text == "itemstandh";
			}
			return true;
		}

		private static bool Usable(BartenderServingFrame frame)
		{
			if ((Object)(object)frame != (Object)null && ((Component)frame).gameObject.activeInHierarchy)
			{
				return R.Zdo(((Component)frame).gameObject) != null;
			}
			return false;
		}

		private static int Number(object value)
		{
			return BartenderPostMarker.Number(value);
		}
	}
	public sealed class LumberjackPostMarker : MonoBehaviour, TextReceiver
	{
		private const string NameKey = "fw_lumberjack_post_name";

		private const string StorageNameKey = "fw_lumberjack_storage_name";

		private const string StoragePositionKey = "fw_lumberjack_storage_pos";

		private const string StorageLinkedKey = "fw_lumberjack_storage_linked";

		private const string StatusKey = "fw_lumberjack_status";

		private const string HarvestedKey = "fw_lumberjack_harvested";

		private static readonly HashSet<LumberjackPostMarker> Active = new HashSet<LumberjackPostMarker>();

		private bool _editStorage;

		private bool _queuedEdit;

		private float _lastInteraction;

		private float _nextUpdate;

		private float _nextHarvest;

		private object _textUser;

		internal string PostName => R.ZString(((Component)this).gameObject, "fw_lumberjack_post_name");

		internal string CurrentStatus => R.ZString(((Component)this).gameObject, "fw_lumberjack_status", "Inactive — no Lumberjack assigned through a Lumberjack Bunk");

		private void OnEnable()
		{
			Active.Add(this);
		}

		private void OnDisable()
		{
			Active.Remove(this);
		}

		private void Start()
		{
			_nextUpdate = Time.time + Random.Range(0.5f, 1.5f);
			_nextHarvest = Time.time + 5f;
		}

		private void LateUpdate()
		{
			if (!_queuedEdit)
			{
				return;
			}
			_queuedEdit = false;
			try
			{
				string text = (_editStorage ? "Wood chest name" : "Lumberjack post name");
				if ((Object)(object)TextInput.instance != (Object)null)
				{
					TextInput.instance.RequestText((TextReceiver)(object)this, text, 32);
				}
				else
				{
					Plugin.Message(_textUser, "The text-entry window is unavailable.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not open Lumberjack Post editor: " + ex.Message));
				Plugin.Message(_textUser, "The Lumberjack Post editor could not be opened.");
			}
		}

		private void Update()
		{
			if (Time.time < _nextUpdate)
			{
				return;
			}
			_nextUpdate = Time.time + 2.5f;
			if (!R.IsOwner(((Component)this).gameObject))
			{
				return;
			}
			if ((Object)(object)JobStationMarker.FindLumberjackBunkFor(this, out var worker) == (Object)null || (Object)(object)worker == (Object)null)
			{
				SetStatus("Inactive — no Lumberjack assigned through a Lumberjack Bunk");
				return;
			}
			string problem;
			NamedContainerMarker namedContainerMarker = Storage(out problem);
			if ((Object)(object)namedContainerMarker == (Object)null)
			{
				SetStatus("Paused — wood chest " + problem);
				return;
			}
			if (Time.time < _nextHarvest)
			{
				float num = Mathf.Ceil(_nextHarvest - Time.time);
				SetStatus($"Active — next beech in {num:0}s");
				return;
			}
			Component val = FindNearestBeech();
			if ((Object)(object)val == (Object)null)
			{
				_nextHarvest = Time.time + 10f;
				SetStatus($"Active — no mature beech trees within {Plugin.LumberjackRadius.Value:0}m");
			}
			else
			{
				ProcessBeech(val, namedContainerMarker);
			}
		}

		internal bool Interact(object user, bool hold, bool alt)
		{
			if (Time.time - _lastInteraction < 0.45f)
			{
				return true;
			}
			_lastInteraction = Time.time;
			if (hold)
			{
				return true;
			}
			_textUser = user;
			_editStorage = alt;
			_queuedEdit = true;
			return true;
		}

		public string GetText()
		{
			if (!_editStorage)
			{
				return PostName;
			}
			return R.ZString(((Component)this).gameObject, "fw_lumberjack_storage_name");
		}

		public void SetText(string text)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			string text2 = PatrolPointMarker.CleanLabel(text);
			R.ClaimOwnership(((Component)this).gameObject);
			if (!_editStorage)
			{
				R.ZSet(((Component)this).gameObject, "fw_lumberjack_post_name", text2);
				R.Call(R.Component(((Component)this).gameObject, "Sign"), "SetText", text2);
				Plugin.Message(_textUser, string.IsNullOrEmpty(text2) ? "Lumberjack Post name cleared." : ("Lumberjack Post named " + text2 + "."));
				return;
			}
			R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_name", text2);
			bool ambiguous;
			NamedContainerMarker namedContainerMarker = NamedContainerMarker.FindNamed(text2, ((Component)this).transform.position, Plugin.LumberjackStorageRange.Value, out ambiguous);
			if ((Object)(object)namedContainerMarker != (Object)null)
			{
				R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_pos", ((Component)namedContainerMarker).transform.position);
				R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_linked", true);
				Plugin.Message(_textUser, "Wood chest linked to " + namedContainerMarker.StorageName + ".");
			}
			else
			{
				R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_linked", false);
				string text3 = (ambiguous ? "More than one nearby chest has that storage name." : (string.IsNullOrEmpty(text2) ? "Wood chest link cleared." : $"No storage named '{text2}' is within {Plugin.LumberjackStorageRange.Value:0}m."));
				Plugin.Message(_textUser, text3);
			}
		}

		internal string HoverText()
		{
			string text = (string.IsNullOrEmpty(PostName) ? "<color=#aaaaaa>Not named</color>" : ("<color=#7CFF7C>" + PostName + "</color>"));
			return "Fuling Lumberjack Post\nName: " + text + "\nWood chest: " + StorageDisplay() + "\n" + $"Range: {Plugin.LumberjackRadius.Value:0}m\nStatus: {CurrentStatus}\n" + string.Format("Beeches processed: {0}\n", R.ZInt(((Component)this).gameObject, "fw_lumberjack_harvested")) + "[<color=yellow><b>E</b></color>] Edit post name\n[<color=yellow><b>Shift + E</b></color>] Set wood chest";
		}

		internal static LumberjackPostMarker FindNamed(string name, Vector3 origin, float radius, out bool ambiguous)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			ambiguous = false;
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			LumberjackPostMarker lumberjackPostMarker = null;
			float num = radius * radius;
			foreach (LumberjackPostMarker item in Active)
			{
				if (!Usable(item) || !string.Equals(item.PostName, name, StringComparison.OrdinalIgnoreCase))
				{
					continue;
				}
				Vector3 val = ((Component)item).transform.position - origin;
				if (!(((Vector3)(ref val)).sqrMagnitude > num))
				{
					if ((Object)(object)lumberjackPostMarker != (Object)null && (Object)(object)lumberjackPostMarker != (Object)(object)item)
					{
						ambiguous = true;
						return null;
					}
					lumberjackPostMarker = item;
				}
			}
			return lumberjackPostMarker;
		}

		internal static LumberjackPostMarker FindAtPosition(Vector3 position)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			LumberjackPostMarker result = null;
			float num = 2.25f;
			foreach (LumberjackPostMarker item in Active)
			{
				if (Usable(item))
				{
					Vector3 val = ((Component)item).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (!(sqrMagnitude >= num))
					{
						num = sqrMagnitude;
						result = item;
					}
				}
			}
			return result;
		}

		private NamedContainerMarker Storage(out string problem)
		{
			//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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			string text = R.ZString(((Component)this).gameObject, "fw_lumberjack_storage_name");
			if (string.IsNullOrEmpty(text))
			{
				problem = "is not configured";
				return null;
			}
			bool flag = R.ZBool(((Component)this).gameObject, "fw_lumberjack_storage_linked");
			Vector3 val = R.ZVector(((Component)this).gameObject, "fw_lumberjack_storage_pos", ((Component)this).transform.position);
			NamedContainerMarker namedContainerMarker = (flag ? NamedContainerMarker.FindAtPosition(val) : null);
			bool ambiguous = false;
			if ((Object)(object)namedContainerMarker == (Object)null)
			{
				namedContainerMarker = NamedContainerMarker.FindNamed(text, ((Component)this).transform.position, Plugin.LumberjackStorageRange.Value, out ambiguous);
			}
			if (ambiguous)
			{
				problem = "name is ambiguous";
				return null;
			}
			if ((Object)(object)namedContainerMarker == (Object)null)
			{
				problem = $"was not found within {Plugin.LumberjackStorageRange.Value:0}m";
				return null;
			}
			if (Vector3.Distance(((Component)this).transform.position, ((Component)namedContainerMarker).transform.position) > Plugin.LumberjackStorageRange.Value)
			{
				problem = $"is more than {Plugin.LumberjackStorageRange.Value:0}m away";
				return null;
			}
			if (R.IsOwner(((Component)this).gameObject))
			{
				if (flag && string.Equals(text, namedContainerMarker.StorageName, StringComparison.Ordinal))
				{
					Vector3 val2 = val - ((Component)namedContainerMarker).transform.position;
					if (!(((Vector3)(ref val2)).sqrMagnitude > 0.01f))
					{
						goto IL_018f;
					}
				}
				R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_name", namedContainerMarker.StorageName);
				R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_pos", ((Component)namedContainerMarker).transform.position);
				R.ZSet(((Component)this).gameObject, "fw_lumberjack_storage_linked", true);
			}
			goto IL_018f;
			IL_018f:
			problem = null;
			return namedContainerMarker;
		}

		private string StorageDisplay()
		{
			string text = R.ZString(((Component)this).gameObject, "fw_lumberjack_storage_name");
			string problem;
			NamedContainerMarker namedContainerMarker = Storage(out problem);
			if ((Object)(object)namedContainerMarker != (Object)null)
			{
				return "<color=#7CFF7C>" + namedContainerMarker.StorageName + "</color>";
			}
			if (string.IsNullOrEmpty(text))
			{
				return "<color=#aaaaaa>Not configured</color>";
			}
			return "<color=#FF7777>" + text + " — " + problem + "</color>";
		}

		private Component FindNearestBeech()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			Component result = null;
			float num = Plugin.LumberjackRadius.Value * Plugin.LumberjackRadius.Value;
			foreach (Component item in R.FindNear("TreeBase", ((Component)this).transform.position, Plugin.LumberjackRadius.Value))
			{
				if (IsMatureBeech(item))
				{
					Vector3 val = item.transform.position - ((Component)this).transform.position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (!(sqrMagnitude >= num))
					{
						num = sqrMagnitude;
						result = item;
					}
				}
			}
			return result;
		}

		private void ProcessBeech(Component tree, NamedContainerMarker storage)
		{
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			Component val = R.Component(((Component)storage).gameObject, "Container");
			if (!R.IsOwner(((Component)storage).gameObject))
			{
				R.ClaimOwnership(((Component)storage).gameObject);
				SetStatus("Active — waiting for ownership of the wood chest");
				return;
			}
			object obj = R.Call(val, "GetInventory");
			GameObject prefab = Plugin.GetPrefab("Wood");
			GameObject prefab2 = Plugin.GetPrefab("Beech_Sapling");
			object obj2 = R.Call(R.Get(R.Component(prefab, "ItemDrop"), "m_itemData"), "Clone");
			int num = Mathf.Max(1, Plugin.LumberjackWoodPerTree.Value);
			if ((Object)(object)val == (Object)null || obj == null || (Object)(object)prefab == (Object)null || (Object)(object)prefab2 == (Object)null || obj2 == null)
			{
				SetStatus("Paused — beech, sapling or wood prefab data is unavailable");
				return;
			}
			R.Set(obj2, "m_stack", num);
			object obj3 = R.Call(obj, "CanAddItem", obj2);
			if (obj3 is bool && !(bool)obj3)
			{
				SetStatus($"Paused — wood chest needs room for {num} Wood");
				return;
			}
			if (obj3 == null)
			{
				object obj4 = R.Call(obj, "HaveEmptySlot");
				if (obj4 is bool && !(bool)obj4)
				{
					SetStatus($"Paused — wood chest needs room for {num} Wood");
					return;
				}
			}
			Vector3 position = tree.transform.position;
			Quaternion rotation = tree.transform.rotation;
			Quaternion val2 = Quaternion.Euler(0f, ((Quaternion)(ref rotation)).eulerAngles.y, 0f);
			GameObject val3 = Object.Instantiate<GameObject>(prefab2, position, val2);
			if ((Object)(object)val3 == (Object)null)
			{
				SetStatus("Paused — could not replant the beech");
				return;
			}
			object obj5 = R.Call(obj, "AddItem", obj2);
			if (!(obj5 is bool) || !(bool)obj5)
			{
				R.DestroyNetworked(val3);
				SetStatus("Paused — wood chest could not accept the harvest");
				return;
			}
			R.Call(val, "Save");
			R.ClaimOwnership(tree.gameObject);
			R.DestroyNetworked(tree.gameObject);
			_nextHarvest = Time.time + Mathf.Max(1f, Plugin.LumberjackWorkInterval.Value);
			R.ZSet(((Component)this).gameObject, "fw_lumberjack_harvested", R.ZInt(((Component)this).gameObject, "fw_lumberjack_harvested") + 1);
			SetStatus($"Active — processed one beech, stored {num} Wood and replanted it");
		}

		private static bool IsMatureBeech(Component tree)
		{
			if ((Object)(object)tree == (Object)null || !tree.gameObject.activeInHierarchy || R.Zdo(tree.gameObject) == null)
			{
				return false;
			}
			string text = R.CleanName(tree.gameObject);
			if (text.StartsWith("Beech", StringComparison.OrdinalIgnoreCase) && text.IndexOf("Sapling", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Log", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Stub", StringComparison.OrdinalIgnoreCase) < 0)
			{
				return text.IndexOf("Stump", StringComparison.OrdinalIgnoreCase) < 0;
			}
			return false;
		}

		private void SetStatus(string status)
		{
			if (!string.Equals(R.ZString(((Component)this).gameObject, "fw_lumberjack_status"), status, StringComparison.Ordinal))
			{
				R.ZSet(((Component)this).gameObject, "fw_lumberjack_status", status);
			}
		}

		private static bool Usable(LumberjackPostMarker post)
		{
			if ((Object)(object)post != (Object)null && ((Component)post).gameObject.activeInHierarchy)
			{
				return R.Zdo(((Component)post).gameObject) != null;
			}
			return false;
		}
	}
	[BepInPlugin("com.sophie.fulingworkforce", "Fuling Workforce", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.sophie.fulingworkforce";

		public const string Name = "Fuling Workforce";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<int> RecruitmentCost;

		internal static ConfigEntry<float> OathRadius;

		internal static ConfigEntry<float> PatrolRange;

		internal static ConfigEntry<float> PatrolLinkDistance;

		internal static ConfigEntry<float> PatrolWaitSeconds;

		internal static ConfigEntry<float> PatrolStuckSeconds;

		internal static ConfigEntry<float> FarmRadius;

		internal static ConfigEntry<float> FarmWorkInterval;

		internal static ConfigEntry<float> FarmStorageRange;

		internal static ConfigEntry<float> JobPostRange;

		internal static ConfigEntry<float> JobWanderRadius;

		internal static ConfigEntry<float> JobWanderMaximumIdle;

		internal static ConfigEntry<float> MachineRadius;

		internal static ConfigEntry<float> MachineWorkInterval;

		internal static ConfigEntry<float> MachineStorageRange;

		internal static ConfigEntry<float> AnimalRadius;

		internal static ConfigEntry<int> AnimalLimit;

		internal static ConfigEntry<float> AnimalWorkInterval;

		internal static ConfigEntry<float> AnimalStorageRange;

		internal static ConfigEntry<float> RepairRadius;

		internal static ConfigEntry<float> RepairWorkInterval;

		internal static ConfigEntry<float> ExpeditionInterval;

		internal static ConfigEntry<float> ExpeditionRadius;

		internal static ConfigEntry<float> ExpeditionTableRange;

		internal static ConfigEntry<float> BartenderRadius;

		internal static ConfigEntry<float> BartenderWorkInterval;

		internal static ConfigEntry<float> BartenderStorageRange;

		internal static ConfigEntry<float> BartenderFrameRange;

		internal static ConfigEntry<float> FireKeeperRadius;

		internal static ConfigEntry<float> FireKeeperWorkInterval;

		internal static ConfigEntry<float> FireKeeperScanInterval;

		internal static ConfigEntry<float> FireKeeperStorageRange;

		internal static ConfigEntry<float> SorterRadius;

		internal static ConfigEntry<float> SorterWorkInterval;

		internal static ConfigEntry<float> SorterStorageRange;

		internal static ConfigEntry<float> LumberjackRadius;

		internal static ConfigEntry<float> LumberjackWorkInterval;

		internal static ConfigEntry<float> LumberjackStorageRange;

		internal static ConfigEntry<int> LumberjackWoodPerTree;

		private Harmony _harmony;

		private Delegate _vanillaReadyHandler;

		private void Awake()
		{
			//IL_0584: Unknown result type (might be due to invalid IL or missing references)
			//IL_058e: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			RecruitmentCost = ((BaseUnityPlugin)this).Config.Bind<int>("Recruitment", "CoinCost", 20, "One-time coin cost to recruit a surrendered Fuling.");
			OathRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Recruitment", "OathTotemRadius", 12f, "Distance from an Oath Totem in which a Fuling may surrender.");
			PatrolRange = ((BaseUnityPlugin)this).Config.Bind<float>("Patrol", "MaximumDistanceFromBunk", 100f, "Every point in a Guard's patrol route must remain within this distance of its bunk.");
			PatrolLinkDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Patrol", "MaximumPointSpacing", 40f, "Maximum straight-line distance between a patrol point and its named next point.");
			PatrolWaitSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Patrol", "WaitAtPointSeconds", 2f, "How long a Guard waits after reaching each patrol point.");
			PatrolStuckSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Patrol", "SkipUnreachablePointSeconds", 45f, "How long a Guard tries to reach a point before skipping to the next one.");
			FarmRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Farming", "WorkRadius", 25f, "How far a Farming Post looks for mature crops.");
			FarmWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Farming", "WorkIntervalSeconds", 2.5f, "Minimum time between crops processed by each active Farming Post.");
			FarmStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("Farming", "StorageRange", 30f, "Maximum distance from a Farming Post to its named storage chest.");
			JobPostRange = ((BaseUnityPlugin)this).Config.Bind<float>("Jobs", "MaximumBunkToPostDistance", 100f, "Maximum distance from a job-specific bunk to its matching named job post.");
			JobWanderRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Jobs", "WorkerWanderRadius", 5f, "How far non-Guard workers wander from their linked job post while appearing to work.");
			JobWanderMaximumIdle = ((BaseUnityPlugin)this).Config.Bind<float>("Jobs", "WorkerWanderMaximumIdleSeconds", 25f, "Maximum random idle time between work-wander destinations.");
			MachineRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Processing", "MachineRadius", 25f, "How far Kiln and Smelter Posts look for compatible machines.");
			MachineWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Processing", "WorkIntervalSeconds", 1.5f, "Minimum time between item transfers by each active Kiln or Smelter Post.");
			MachineStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("Processing", "StorageRange", 30f, "Maximum distance from a Kiln or Smelter Post to each named storage chest.");
			AnimalRadius = ((BaseUnityPlugin)this).Config.Bind<float>("AnimalKeeper", "WorkRadius", 20f, "How far an Animal Keeper Post manages supported tamed animals and collects their drops.");
			AnimalLimit = ((BaseUnityPlugin)this).Config.Bind<int>("AnimalKeeper", "PopulationLimitPerSpecies", 10, "Population limit applied separately to boars, wolves and lox within an Animal Keeper Post's radius.");
			AnimalWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("AnimalKeeper", "WorkIntervalSeconds", 2.5f, "Minimum time between Animal Keeper Post feeding, culling or collection actions.");
			AnimalStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("AnimalKeeper", "StorageRange", 30f, "Maximum distance from an Animal Keeper Post to its named food and products chests.");
			RepairRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Repairer", "WorkRadius", 25f, "How far a Repair Post looks for damaged player-built structures.");
			RepairWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Repairer", "WorkIntervalSeconds", 2.5f, "Minimum time between structures repaired by each active Repair Post.");
			ExpeditionInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Expedition", "ReportIntervalSeconds", 600f, "Seconds between landmark reports from each active Expedition Post.");
			ExpeditionRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Expedition", "DiscoveryRadius", 2000f, "Maximum distance from an Expedition Post at which Explorers can discover landmarks.");
			ExpeditionTableRange = ((BaseUnityPlugin)this).Config.Bind<float>("Expedition", "CartographyTableRange", 15f, "Maximum distance from an Expedition Post to its linked cartography table.");
			BartenderRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Bartender", "FermenterRadius", 25f, "How far a Bartender Post looks for fermenters.");
			BartenderWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Bartender", "WorkIntervalSeconds", 1.5f, "Minimum time between fermenter, collection or serving-frame actions by each Bartender Post.");
			BartenderStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("Bartender", "StorageRange", 30f, "Maximum distance from a Bartender Post to its mead base and finished mead chests.");
			BartenderFrameRange = ((BaseUnityPlugin)this).Config.Bind<float>("Bartender", "ServingFrameRange", 25f, "Maximum distance from a Bartender Post to a registered serving item frame.");
			FireKeeperRadius = ((BaseUnityPlugin)this).Config.Bind<float>("FireKeeper", "FireRadius", 100f, "How far a Fire Keeper Post looks for fireplaces, hearths, braziers and other fuelled fires.");
			FireKeeperWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("FireKeeper", "WorkIntervalSeconds", 1.5f, "Minimum time between fuel additions by each active Fire Keeper Post.");
			FireKeeperScanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("FireKeeper", "FireScanIntervalSeconds", 15f, "How often a Fire Keeper Post refreshes its cached list of fires. Larger values reduce scanning cost.");
			FireKeeperStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("FireKeeper", "StorageRange", 30f, "Maximum distance from a Fire Keeper Post to its named fuel chest.");
			SorterRadius = ((BaseUnityPlugin)this).Config.Bind<float>("ChestSorter", "ControlledChestRadius", 30f, "How far a Chest Sorter Post looks for explicitly controlled destination chests.");
			SorterWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("ChestSorter", "WorkIntervalSeconds", 0.75f, "Minimum time between stack transfers by each active Chest Sorter Post.");
			SorterStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("ChestSorter", "InputChestRange", 30f, "Maximum distance from a Chest Sorter Post to its named input chest.");
			LumberjackRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Lumberjack", "WorkRadius", 30f, "How far a Lumberjack Post looks for mature beech trees.");
			LumberjackWorkInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Lumberjack", "WorkIntervalSeconds", 60f, "Minimum time between beech trees processed by each active Lumberjack Post.");
			LumberjackStorageRange = ((BaseUnityPlugin)this).Config.Bind<float>("Lumberjack", "StorageRange", 30f, "Maximum distance from a Lumberjack Post to its named wood chest.");
			LumberjackWoodPerTree = ((BaseUnityPlugin)this).Config.Bind<int>("Lumberjack", "WoodPerTree", 20, "Wood placed in the output chest for each mature beech tree processed.");
			_harmony = new Harmony("com.sophie.fulingworkforce");
			WorkforcePatches.Apply(_harmony);
			SubscribeToJotunn();
			Log.LogInfo((object)"Fuling Workforce 1.0.0 loaded");
		}

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

		private void SubscribeToJotunn()
		{
			EventInfo eventInfo = R.Type("Jotunn.Managers.PrefabManager")?.GetEvent("OnVanillaPrefabsAvailable", BindingFlags.Static | BindingFlags.Public);
			if (eventInfo == null)
			{
				Log.LogError((object)"Jotunn PrefabManager event was not found.");
				return;
			}
			_vanillaReadyHandler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, "RegisterContent");
			eventInfo.AddEventHandler(null, _vanillaReadyHandler);
		}

		public void RegisterContent()
		{
			try
			{
				AddControllerToFulings();
				AddPiece("FW_OathTotem", "guard_stone", "Fuling Oath Totem", "Fulings reduced below 10% health within its radius surrender and may be recruited for coins.", typeof(OathTotemMarker), Req("FineWood", 10), Req("BlackMetal", 5), Req("GoblinTotem", 1));
				AddPiece("FW_GuardPost", "bed", "Fuling Guard Bunk", "Assign a Guard and optionally enter the name of a starting Patrol Point with Shift + E.", typeof(JobStationMarker), "Guard", Req("Wood", 20), Req("BlackMetal", 2));
				AddPiece("FW_PatrolPoint", "sign", "Fuling Patrol Point", "Name this point and the next point in the route. Linked points may be up to 40 metres apart.", typeof(PatrolPointMarker), Req("Wood", 2), Req("Resin", 1));
				AddPiece("FW_FarmBunk", "bed", "Fuling Farming Bunk", "Assign a Farmer and use Shift + E to enter the name of its Farming Post.", typeof(JobStationMarker), "Farmer", Req("Wood", 20), Req("IronNails", 10));
				AddPiece("FW_FarmingPost", "sign", "Fuling Farming Post", "Name the post with E and link a named storage chest with Shift + E. An assigned Farmer activates it.", typeof(FarmingPostMarker), Req("Wood", 10), Req("FineWood", 5), Req("IronNails", 5));
				AddPiece("FW_KilnBunk", "bed", "Fuling Kiln Bunk", "Assign a Kiln Operator and use Shift + E to enter the name of its Kiln Post.", typeof(JobStationMarker), "Kiln", Req("Wood", 20), Req("SurtlingCore", 1));
				AddPiece("FW_KilnPost", "sign", "Fuling Kiln Post", "Operates nearby charcoal kilns using separately named wood and coal chests.", typeof(MachinePostMarker), "Kiln", Req("Wood", 10), Req("FineWood", 5), Req("SurtlingCore", 1));
				AddPiece("FW_SmelterBunk", "bed", "Fuling Smelter Bunk", "Assign a Smelter Operator and use Shift + E to enter the name of its Smelter Post.", typeof(JobStationMarker), "Smelter", Req("Wood", 20), Req("IronNails", 10));
				AddPiece("FW_SmelterPost", "sign", "Fuling Smelter Post", "Operates nearby smelters and blast furnaces using separate ore, coal and output chests.", typeof(MachinePostMarker), "Smelter", Req("Wood", 10), Req("FineWood", 5), Req("Iron", 5), Req("SurtlingCore", 2));
				AddPiece("FW_KeeperBunk", "bed", "Fuling Animal Keeper Bunk", "Assign an Animal Keeper and use Shift + E to enter the name of its Animal Keeper Post.", typeof(JobStationMarker), "Keeper", Req("Wood", 20), Req("LeatherScraps", 10), Req("IronNails", 10));
				AddPiece("FW_KeeperPost", "sign", "Fuling Animal Keeper Post", "Feeds and manages nearby tamed boars, wolves and lox using separate food and products chests.", typeof(AnimalKeeperPostMarker), Req("Wood", 10), Req("FineWood", 5), Req("IronNails", 5));
				AddPiece("FW_RepairBunk", "bed", "Fuling Repairer Bunk", "Assign a Repairer and use Shift + E to enter the name of its Repair Post.", typeof(JobStationMarker), "Repairer", Req("Wood", 20), Req("IronNails", 10));
				AddPiece("FW_RepairPost", "sign", "Fuling Repair Post", "Repairs nearby damaged player-built structures while a linked Repairer is assigned.", typeof(RepairPostMarker), Req("Wood", 10), Req("FineWood", 5), Req("Iron", 5));
				AddPiece("FW_ExpeditionBunk", "bed", "Fuling Expedition Manager Bunk", "Assign an Expedition Manager and use Shift + E to enter the name of its Expedition Post.", typeof(JobStationMarker), "Expedition", Req("Wood", 20), Req("FineWood", 10), Req("DeerHide", 5), Req("IronNails", 10));
				AddPiece("FW_ExpeditionPost", "sign", "Fuling Expedition Post", "Enlists up to ten Explorers and records discovered landmarks for a linked cartography table.", typeof(ExpeditionPostMarker), Req("Wood", 10), Req("FineWood", 10), Req("Bronze", 5));
				AddPiece("FW_BartenderBunk", "bed", "Fuling Bartender Bunk", "Assign a Bartender and use Shift + E to enter the name of its Bartender Post.", typeof(JobStationMarker), "Bartender", Req("Wood", 20), Req("FineWood", 10), Req("DeerHide", 5), Req("Bronze", 2));
				AddPiece("FW_BartenderPost", "sign", "Fuling Bartender Post", "Operates nearby fermenters and stocks registered item frames from a finished mead chest.", typeof(BartenderPostMarker), Req("Wood", 10), Req("FineWood", 10), Req("Bronze", 5));
				AddPiece("FW_FireKeeperBunk", "bed", "Fuling Fire Keeper Bunk", "Assign a Fire Keeper and use Shift + E to enter the name of its Fire Keeper Post.", typeof(JobStationMarker), "FireKeeper", Req("Wood", 20), Req("FineWood", 5), Req("SurtlingCore", 2));
				AddPiece("FW_FireKeeperPost", "sign", "Fuling Fire Keeper Post", "Uses a named fuel chest to keep compatible fuelled fires supplied within 100 metres.", typeof(FireKeeperPostMarker), Req("Wood", 10), Req("FineWood", 5), Req("SurtlingCore", 2));
				AddPiece("FW_SorterBunk", "bed", "Fuling Chest Sorter Bunk", "Assign a Chest Sorter and use Shift + E to enter the name of its Chest Sorter Post.", typeof(JobStationMarker), "Sorter", Req("Wood", 20), Req("FineWood", 10), Req("IronNails", 10));
				AddPiece("FW_SorterPost", "sign", "Fuling Chest Sorter Post", "Moves input items only into controlled nearby chests that already contain the same item.", typeof(ChestSorterPostMarker), Req("Wood", 10), Req("FineWood", 10), Req("Iron", 5));
				AddPiece("FW_LumberjackBunk", "bed", "Fuling Lumberjack Bunk", "Assign a Lumberjack and use Shift + E to enter the name of its Lumberjack Post.", typeof(JobStationMarker), "Lumberjack", Req("Wood", 20), Req("FineWood", 5), Req("IronNails", 10));
				AddPiece("FW_LumberjackPost", "sign", "Fuling Lumberjack Post", "Processes one mature beech per minute, stores its wood and replants it for free.", typeof(LumberjackPostMarker), Req("Wood", 10), Req("FineWood", 5), Req("Bronze", 5));
				Log.LogInfo((object)"Registered recruitment, Guard patrols, Farming, Kiln, Smelter, Animal Keeper, Repairer, Expedition, Bartender, Fire Keeper, Chest Sorter and Lumberjack job workflows.");
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Content registration failed: {arg}");
			}
		}

		private static Tuple<string, int> Req(string item, int amount)
		{
			return Tuple.Create(item, amount);
		}

		private static void AddControllerToFulings()
		{
			GameObject prefab = GetPrefab("Goblin");
			if (!((Object)(object)prefab == (Object)null) && (Object)(object)prefab.GetComponent<FulingWorker>() == (Object)null)
			{
				prefab.AddComponent<FulingWorker>();
			}
		}

		private static void AddPiece(string internalName, string basePrefab, string name, string description, Type markerType, params object[] roleAndRequirements)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			string job = roleAndRequirements.FirstOrDefault() as string;
			IEnumerable<Tuple<string, int>> enumerable = roleAndRequirements.OfType<Tuple<string, int>>();
			object obj = R.Call(R.StaticProperty(R.Type("Jotunn.Managers.PrefabManager"), "Instance"), "CreateClonedPrefab", internalName, basePrefab);
			GameObject val = (GameObject)((obj is GameObject) ? obj : null);
			if ((Object)(object)val == (Object)null)
			{
				throw new InvalidOperationException("Could not clone " + basePrefab);
			}
			Component val2 = val.GetComponent(markerType) ?? val.AddComponent(markerType);
			if (val2 is JobStationMarker jobStationMarker)
			{
				jobStationMarker.Job = job;
				val.transform.localScale = val.transform.localScale * 0.78f;
			}
			if (val2 is PatrolPointMarker)
			{
				val.transform.localScale = val.transform.localScale * 0.7f;
			}
			if (val2 is FarmingPostMarker)
			{
				val.transform.localScale = val.transform.localScale * 0.8f;
			}
			if (val2 is MachinePostMarker machinePostMarker)
			{
				machinePostMarker.Job = job;
				val.transform.localScale = val.transform.localScale * 0.8f;
			}
			if (val2 is AnimalKeeperPostMarker)
			{
				val.transform.localScale = val.transform.localScale * 0.8f;
			}
			if (val2 is RepairPostMarker)
			{
				val.transform.localScale = val.transform.localScale * 0.8f;
			}
			if (val2 is ExpeditionPostMarker)
			{
				val.transform.localScale = val.transform.localScale * 0.8f;
			}
			if (val2 is BartenderPostMarker)
			{
				val.transform.localScale = val.transform.localScale * 0.8f;
			}
			if (val2 is FireKeeperPostMarker || val2 is ChestSorterPostMarker || val2 is LumberjackPostMarker)
			{
				val.transform.localScale = val.transform.localScale * 0.8f;
			}
			if (val2 is OathTotemMarker)
			{
				Type type = R.Type("PrivateArea");
				Component val3 = ((type == null) ? null : val.GetComponent(type));
				if ((Object)(object)val3 != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val3);
				}
			}
			Type type2 = R.Type("Jotunn.Configs.PieceConfig");
			object obj2 = Activator.CreateInstance(type2);
			R.Property(obj2, "Name", name);
			R.Property(obj2, "Description", description);
			R.Property(obj2, "PieceTable", "Hammer");
			R.Property(obj2, "Category", "Furniture");
			Type type3 = R.Type("Jotunn.Configs.RequirementConfig");
			Array array = Array.CreateInstance(type3, enumerable.Count());
			int num = 0;
			foreach (Tuple<string, int> item in enumerable)
			{
				object value = Activator.CreateInstance(type3, item.Item1, item.Item2, 0, true);
				array.SetValue(value, num++);
			}
			R.Property(obj2, "Requirements", array);
			object obj3 = R.Type("Jotunn.Entities.CustomPiece").GetConstructor(new Type[3]
			{
				typeof(GameObject),
				typeof(bool),
				type2
			}).Invoke(new object[3] { val, false, obj2 });
			R.Call(R.StaticProperty(R.Type("Jotunn.Managers.PieceManager"), "Instance"), "AddPiece", obj3);
		}

		internal static GameObject GetPrefab(string name)
		{
			object obj = R.Call(R.StaticProperty(R.Type("Jotunn.Managers.PrefabManager"), "Instance"), "GetPrefab", name);
			return (GameObject)((obj is GameObject) ? obj : null);
		}

		internal static void Message(object humanoid, string text)
		{
			try
			{
				object obj = Enum.Parse(R.Type("MessageHud+MessageType"), "Center");
				MethodInfo methodInfo = humanoid.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Message" && m.GetParameters().Length >= 2);
				if (methodInfo != null)
				{
					R.InvokeWithDefaults(methodInfo, humanoid, obj, text);
					return;
				}
			}
			catch
			{
			}
			Log.LogInfo((object)text);
		}
	}
	internal static class R
	{
		private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

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

		internal static Type Type(string name)
		{
			if (TypeCache.TryGetValue(name, out var value))
			{
				return value;
			}
			Type type = AccessTools.TypeByName(name);
			if (type != null)
			{
				TypeCache[name] = type;
			}
			return type;
		}

		internal static object StaticProperty(Type type, string name)
		{
			if (type == null)
			{
				return null;
			}
			object obj = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null);
			if (obj == null)
			{
				FieldInfo? field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if ((object)field == null)
				{
					return null;
				}
				obj = field.GetValue(null);
			}
			return obj;
		}

		internal static object Get(object instance, string name)
		{
			if (instance == null)
			{
				return null;
			}
			Type type = instance.GetType();
			object obj = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance);
			if (obj == null)
			{
				PropertyInfo? property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if ((object)property == null)
				{
					return null;
				}
				obj = property.GetValue(instance, null);
			}
			return obj;
		}

		internal static bool Set(object instance, string name, object value)
		{
			if (instance == null)
			{
				return false;
			}
			Type type = instance.GetType();
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				field.SetValue(instance, value);
				return true;
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.CanWrite)
			{
				property.SetValue(instance, value, null);
				return true;
			}
			return false;
		}

		internal static void Property(object instance, string name, object value)
		{
			PropertyInfo? property = instance.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property == null)
			{
				throw new MissingMemberException(instance.GetType().FullName, name);
			}
			property.SetValue(instance, value, null);
		}

		internal static object Call(object instance, string name, params object[] args)
		{
			if (instance == null)
			{
				return null;
			}
			MethodInfo methodInfo = BestMethod(instance.GetType(), name, args);
			if (!(methodInfo == null))
			{
				return InvokeWithDefaults(methodInfo, instance, args);
			}
			return null;
		}

		internal static object CallStatic(Type type, string name, params object[] args)
		{
			MethodInfo methodInfo = BestMethod(type, name, args, requireStatic: true);
			if (!(methodInfo == null))
			{
				return InvokeWithDefaults(methodInfo, null, args);
			}
			return null;
		}

		internal static MethodInfo BestMethod(Type type, string name, object[] args, bool requireStatic = false)
		{
			if (type == null)
			{
				return null;
			}
			return (from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
				where m.Name == name && (!requireStatic || m.IsStatic)
				orderby Math.Abs(m.GetParameters().Length - args.Length)
				select m).FirstOrDefault((MethodInfo m) => Compatible(m.GetParameters(), args));
		}

		private static bool Compatible(ParameterInfo[] parameters, object[] args)
		{
			if (args.Length > parameters.Length)
			{
				return false;
			}
			for (int i = 0; i < args.Length; i++)
			{
				if (args[i] != null)
				{
					Type type = parameters[i].ParameterType;
					if (type.IsByRef)
					{
						type = type.GetElementType();
					}
					if (!type.IsInstanceOfType(args[i]) && !CanConvert(args[i].GetType(), type))
					{
						return false;
					}
				}
			}
			return parameters.Skip(args.Length).All((ParameterInfo p) => p.IsOptional || p.HasDefaultValue || !p.ParameterType.IsValueType);
		}

		private static bool CanConvert(Type source, Type target)
		{
			if (!(source == target))
			{
				if (source.IsPrimitive)
				{
					return target.IsPrimitive;
				}
				return false;
			}
			return true;
		}

		internal static object InvokeWithDefaults(MethodInfo method, object instance, params object[] supplied)
		{
			ParameterInfo[] parameters = method.GetParameters();
			object[] array = new object[parameters.Length];
			for (int i = 0; i < parameters.Length; i++)
			{
				if (i < supplied.Length)
				{
					array[i] = ConvertValue(supplied[i], parameters[i].ParameterType);
					continue;
				}
				if (parameters[i].HasDefaultValue)
				{
					array[i] = parameters[i].DefaultValue;
					continue;
				}
				Type type = (parameters[i].ParameterType.IsByRef ? parameters[i].ParameterType.GetElementType() : parameters[i].ParameterType);
				array[i] = (type.IsValueType ? Activator.CreateInstance(type) : null);
			}
			return method.Invoke(instance, array);
		}

		private static object ConvertValue(object value, Type target)
		{
			if (value == null)
			{
				return null;
			}
			if (target.IsByRef)
			{
				target = target.GetElementType();
			}
			if (target.IsInstanceOfType(value))
			{
				return value;
			}
			try
			{
				return Convert.ChangeType(value, target);
			}
			catch
			{
				return value;
			}
		}

		internal static Component Component(GameObject gameObject, string typeName)
		{
			Type type = Type(typeName);
			if (!(type == null) && !((Object)(object)gameObject == (Object)null))
			{
				return gameObject.GetComponent(type);
			}
			return null;
		}

		internal static Object[] Find(string typeName)
		{
			Type type = Type(typeName);
			if (!(type == null))
			{
				return Object.FindObjectsOfType(type);
			}
			return (Object[])(object)new Object[0];
		}

		internal static IEnumerable<Component> FindNear(string typeName, Vector3 centre, float radius)
		{
			//IL_000f: 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)
			Type type = Type(typeName);
			if (type == null)
			{
				yield break;
			}
			HashSet<int> seen = new HashSet<int>();
			Collider[] colliders = Physics.OverlapSphere(centre, radius);
			int i = 0;
			while (i < colliders.Length)
			{
				Collider val = colliders[i];
				if (!((Object)(object)val == (Object)null))
				{
					Component val2 = ((Component)val).GetComponentInParent(type);
					if ((Object)(object)val2 == (Object)null)
					{
						val2 = ((Component)val).GetComponent(type);
					}
					if ((Object)(object)val2 != (Object)null && seen.Add(((Object)val2).GetInstanceID()))
					{
						yield return val2;
					}
				}
				int num = i + 1;
				i = num;
			}
		}

		internal static object Zdo(GameObject gameObject)
		{
			return Call(Component(gameObject, "ZNetView"), "GetZDO");
		}

		internal static bool IsOwner(GameObject gameObject)
		{
			object obj = Call(Component(gameObject, "ZNetView"), "IsOwner");
			bool flag = default(bool);
			int num;
			if (obj is bool)
			{
				flag = (bool)obj;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		internal static void ClaimOwnership(GameObject gameObject)
		{
			object instance = Component(gameObject, "ZNetView");
			object obj = Call(instance, "IsOwner");
			if (obj is bool && !(bool)obj)
			{
				Call(instance, "ClaimOwnership");
			}
		}

		internal static bool ZBool(GameObject gameObject, string key, bool fallback = false)
		{
			object obj = ZGet(gameObject, "GetBool", key, fallback);
			if (obj is bool)
			{
				return (bool)obj;
			}
			return fallback;
		}

		internal static string ZString(GameObject gameObject, string key, string fallback = "")
		{
			return (ZGet(gameObject, "GetString", key, fallback) as string) ?? fallback;
		}

		internal static int ZInt(GameObject gameObject, string key, int fallback = 0)
		{
			object obj = ZGet(gameObject, "GetInt", key, fallback);
			if (obj is int)
			{
				return (int)obj;
			}
			return fallback;
		}

		internal static float ZFloat(GameObject gameObject, string key, float fallback = 0f)
		{
			object obj = ZGet(gameObject, "GetFloat", key, fallback);
			if (obj is float)
			{
				return (float)obj;
			}
			return fallback;
		}

		internal static Vector3 ZVector(GameObject gameObject, string key, Vector3 fallback)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			object obj = ZGet(gameObject, "GetVec3", key, fallback);
			if (obj is Vector3)
			{
				return (Vector3)obj;
			}
			return fallback;
		}

		internal static void ZSet(GameObject gameObject, string key, object value)
		{
			object obj = Zdo(gameObject);
			if (obj == null)
			{
				return;
			}
			object[] supplied = new object[2]
			{
				StableHash(key),
				value
			};
			MethodInfo methodInfo = HashedMethod(obj.GetType(), "Set", value);
			if (methodInfo != null)
			{
				InvokeWithDefaults(methodInfo, obj, supplied);
				return;
			}
			MethodInfo methodInfo2 = KeyedMethod(obj.GetType(), "Set", typeof(string), value);
			if (methodInfo2 != null)
			{
				InvokeWithDefaults(methodInfo2, obj, key, value);
			}
		}

		private static object ZGet(GameObject gameObject, string methodName, string key, object fallback)
		{
			object obj = Zdo(gameObject);
			if (obj == null)
			{
				return fallback;
			}
			object[] supplied = new object[2]
			{
				StableHash(key),
				fallback
			};
			MethodInfo methodInfo = HashedMethod(obj.GetType(), methodName, fallback);
			if (methodInfo != null)
			{
				return InvokeWithDefaults(methodInfo, obj, supplied);
			}
			MethodInfo methodInfo2 = KeyedMethod(obj.GetType(), methodName, typeof(string), fallback);
			if (!(methodInfo2 != null))
			{
				return fallback;
			}
			return InvokeWithDefaults(methodInfo2, obj, key, fallback);
		}

		private static MethodInfo HashedMethod(Type type, string name, object value)
		{
			return KeyedMethod(type, name, typeof(int), value);
		}

		private static MethodInfo KeyedMethod(Type type, string name, Type keyType, object value)
		{
			Type valueType = value?.GetType();
			return type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method)
			{
				if (method.Name != name)
				{
					return false;
				}
				ParameterInfo[] parameters = method.GetParameters();
				if (parameters.Length != 2 || parameters[0].ParameterType != keyType)
				{
					return false;
				}
				Type type2 = parameters[1].ParameterType;
				if (type2.IsByRef)
				{
					type2 = type2.GetElementType();
				}
				return (value != null) ? (type2 == valueType || type2.IsInstanceOfType(value)) : (!type2.IsValueType);
			});
		}

		internal static int StableHash(string text)
		{
			int num = 5381;
			int num2 = num;
			for (int i = 0; i < text.Length && text[i] != 0; i += 2)
			{
				num = ((num << 5) + num) ^ text[i];
				if (i == text.Length - 1 || text[i + 1] == '\0')
				{
					break;
				}
				num2 = ((num2 << 5) + num2) ^ text[i + 1];
			}
			return num + num2 * 1566083941;
		}

		internal static void DestroyNetworked(GameObject gameObject)
		{
			if (!((Object)(object)gameObject == (Object)null))
			{
				Type type = Type("ZNetScene");
				object obj = StaticProperty(type, "instance") ?? StaticProperty(type, "m_instance");
				if (obj != null)
				{
					Call(obj, "Destroy", gameObject);
				}
				else
				{
					Object.Destroy((Object)(object)gameObject);
				}
			}
		}

		internal static string CleanName(GameObject go)
		{
			if (!((Object)(object)go == (Object)null))
			{
				return ((Object)go).name.Replace("(Clone)", "").Trim();
			}
			return "";
		}
	}
	public sealed class FireKeeperPostMarker : MonoBehaviour, TextReceiver
	{
		private const string NameKey = "fw_firekeeper_post_name";

		private const string StorageNameKey = "fw_firekeeper_storage_name";

		private const string StoragePositionKey = "fw_firekeeper_storage_pos";

		private const string StorageLinkedKey = "fw_firekeeper_storage_linked";

		private const string StatusKey = "fw_firekeeper_status";

		private const string FueledKey = "fw_firekeeper_fueled";

		private static readonly HashSet<FireKeeperPostMarker> Active = new HashSet<FireKeeperPostMarker>();

		private readonly List<Component> _fires = new List<Component>();

		private bool _editStorage;

		private bool _queuedEdit;

		private float _lastInteraction;

		private float _nextScan;

		private float _nextWork;

		private int _fireCursor;

		private object _textUser;

		internal string PostName => R.ZString(((Component)this).gameObject, "fw_firekeeper_post_name");

		internal string CurrentStatus => R.ZString(((Component)this).gameObject, "fw_firekeeper_status", "Inactive — no Fire Keeper assigned through a Fire Keeper Bunk");

		private void OnEnable()
		{
			Active.Add(this);
		}

		private void OnDisable()
		{
			Active.Remove(this);
		}

		private void Start()
		{
			_nextWork = Time.time + Random.Range(0.5f, 1.5f);
			_nextScan = 0f;
		}

		private void LateUpdate()
		{
			if (!_queuedEdit)
			{
				return;
			}
			_queuedEdit = false;
			try
			{
				string text = (_editStorage ? "Fuel chest name" : "Fire Keeper post name");
				if ((Object)(object)TextInput.instance != (Object)null)
				{
					TextInput.instance.RequestText((TextReceiver)(object)this, text, 32);
				}
				else
				{
					Plugin.Message(_textUser, "The text-entry window is unavailable.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not open Fire Keeper Post editor: " + ex.Message));
				Plugin.Message(_textUser, "The Fire Keeper Post editor could not be opened.");
			}
		}

		private void Update()
		{
			if (Time.time < _nextWork)
			{
				return;
			}
			_nextWork = Time.time + Mathf.Max(0.5f, Plugin.FireKeeperWorkInterval.Value);
			if (!R.IsOwner(((Component)this).gameObject))
			{
				return;
			}
			if ((Object)(object)JobStationMarker.FindFireKeeperBunkFor(this, out var worker) == (Object)null || (Object)(object)worker == (Object)null)
			{
				SetStatus("Inactive — no Fire Keeper assigned through a Fire Keeper Bunk");
				return;
			}
			string problem;
			NamedContainerMarker namedContainerMarker = Storage(out problem);
			if ((Object)(object)namedContainerMarker == (Object)null)
			{
				SetStatus("Paused — fuel chest " + problem);
				return;
			}
			if (Time.time >= _nextScan)
			{
				RefreshFires();
			}
			if (_fires.Count == 0)
			{
				SetStatus($"Active — no fuelled fires within {Plugin.FireKeeperRadius.Value:0}m");
				return;
			}
			object inventory = namedContainerMarker.Inventory;
			bool flag = false;
			bool flag2 = false;
			for (int i = 0; i < _fires.Count; i++)
			{
				int num = (_fireCursor + i) % _fires.Count;
				Component fire = _fires[num];
				if (!UsableFire(fire) || !NeedsFuel(fire))
				{
					continue;
				}
				flag = true;
				object obj = FindFuel(inventory, fire);
				if (obj != null)
				{
					flag2 = true;
					if (TryAddFuel(fire))
					{
						RemoveOne(namedContainerMarker, inventory, obj);
						_fireCursor = (num + 1) % _fires.Count;
						R.ZSet(((Component)this).gameObject, "fw_firekeeper_fueled", R.ZInt(((Component)this).gameObject, "fw_firekeeper_fueled") + 1);
						SetStatus("Active — added " + ItemDisplayName(obj) + " to " + FireDisplayName(fire));
						return;
					}
				}
			}
			if (!flag)
			{
				SetStatus($"Active — all {_fires.Count} nearby fires are above the 1-fuel reserve");
			}
			else if (!flag2)
			{
				SetStatus("Paused — fuel chest needs fuel accepted by nearby fires");
			}
			else
			{
				SetStatus("Paused — could not add fuel to a nearby fire");
			}
		}

		internal bool Interact(object user, bool hold, bool alt)
		{
			if (Time.time - _lastInteraction < 0.45f)
			{
				return true;
			}
			_lastInteraction = Time.time;
			if (hold)
			{
				return true;
			}
			_textUser = user;
			_editStorage = alt;
			_queuedEdit = true;
			return true;
		}

		public string GetText()
		{
			if (!_editStorage)
			{
				return PostName;
			}
			return R.ZString(((Component)this).gameObject, "fw_firekeeper_storage_name");
		}

		public void SetText(string text)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			string text2 = PatrolPointMarker.CleanLabel(text);
			R.ClaimOwnership(((Component)this).gameObject);
			if (!_editStorage)
			{
				R.ZSet(((Component)this).gameObject, "fw_firekeeper_post_name", text2);
				R.Call(R.Component(((Component)this).gameObject, "Sign"), "SetText", text2);
				Plugin.Message(_textUser, string.IsNullOrEmpty(text2) ? "Fire Keeper Post name cleared." : ("Fire Keeper Post named " + text2 + "."));
				return;
			}
			R.ZSet(((Component)this).gameObject, "fw_firekeeper_storage_name", text2);
			bool ambiguous;
			NamedContainerMarker namedContainerMarker = NamedContainerMarker.FindNamed(text2, ((Component)this).transform.position, Plugin.FireKeeperStorageRange.Value, out ambiguous);
			if ((Object)(object)namedContainerMarker != (Object)null)
			{
				R.ZSet(((Component)this).gameObject, "fw_firekeeper_storage_pos", ((Component)namedContainerMarker).transform.position);
				R.ZSet(((Component)this).gameObject, "fw_firekeeper_storage_linked", true);
				Plugin.Message(_textUser, "Fuel chest linked to " + namedContainerMarker.StorageName + ".");
			}
			else
			{
				R.ZSet(((Component)this).gameObject, "fw_firekeeper_storage_linked", false);
				string text3 = (ambiguous ? "More than one nearby chest has that storage name." : (string.IsNullOrEmpty(text2) ? "Fuel chest link cleared." : $"No storage named '{text2}' is within {Plugin.FireKeeperStorageRange.Value:0}m."));
				Plugin.Message(_textUser, text3);
			}
		}

		internal string HoverText()
		{
			string text = (string.IsNullOrEmpty(PostName) ? "<color=#aaaaaa>Not named</color>" : ("<color=#7CFF7C>" + PostName + "</color>"));
			return "Fuling Fire Keeper Post\nName: " + text + "\nFuel chest: " + StorageDisplay() + "\n" + string.Format("Range: {0:0}m\nStatus: {1}\nFires fueled: {2}\n", Plugin.FireKeeperRadius.Value, CurrentStatus, R.ZInt(((Component)this).gameObject, "fw_firekeeper_fueled")) + "[<color=yellow><b>E</b></color>] Edit post name\n[<color=yellow><b>Shift + E</b></color>] Set fuel chest";
		}

		internal static FireKeeperPostMarker FindNamed(string name, Vector3 origin, float radius, out bool ambiguous)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			ambiguous = false;
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			FireKeeperPostMarker fireKeeperPostMarker = null;
			float num = radius * radius;
			foreach (FireKeeperPostMarker item in Active)
			{
				if (!Usable(item) || !string.Equals(item.PostName, name, StringComparison.OrdinalIgnoreCase))
				{
					continue;
				}
				Vector3 val = ((Component)item).transform.position - origin;
				if (!(((Vector3)(ref val)).sqrMagnitude > num))
				{
					if ((Object)(object)fireKeeperPostMarker != (Object)null && (Object)(object)fireKeeperPostMarker != (Object)(object)item)
					{
						ambiguous = true;
						return null;
					}
					fireKeeperPostMarker = item;
				}
			}
			return fireKeeperPostMarker;
		}

		internal static FireKeeperPostMarker FindAtPosition(Vector3 position)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			FireKeeperPostMarker result = null;
			float num = 2.25f;
			foreach (FireKeeperPostMarker item in Active)
			{
				if (Usable(item))
				{
					Vector3 val = ((Component)item).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (!(sqrMagnitude >= num))
					{
						num = sqrMagnitude;
						result = item;
					}
				}
			}
			return result;
		}

		private NamedContainerMarker Storage(out string problem)
		{
			//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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			string text = R.ZString(((Component)this).g