Decompiled source of WorkshopWorker v1.0.2

BepInEx/plugins/WorkshopWorker/WorkshopWorker.dll

Decompiled an hour ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Daxtillion")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Valheim quality-of-life improvements for storage, crafting, production and repairs.")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2")]
[assembly: AssemblyProduct("Workshop Worker")]
[assembly: AssemblyTitle("WorkshopWorker")]
[assembly: AssemblyVersion("1.0.2.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 WorkshopWorker
{
	internal static class AccessPolicy
	{
		internal static bool Busy(bool owner, bool localUse, bool replicatedUse, bool exactOpen, bool wagon)
		{
			if (!wagon)
			{
				if (!owner)
				{
					return replicatedUse;
				}
				if (localUse)
				{
					return !exactOpen;
				}
				return false;
			}
			return true;
		}
	}
	internal sealed class OpenTicket
	{
		internal readonly long Peer;

		internal readonly float Started;

		internal bool Answered;

		internal bool Granted;

		internal int Frame;

		internal OpenTicket(long peer, float started)
		{
			Peer = peer;
			Started = started;
		}

		internal bool Accept(long peer, bool granted, int frame)
		{
			if (Answered || peer != Peer)
			{
				return false;
			}
			Answered = true;
			Granted = granted;
			Frame = frame;
			return true;
		}

		internal bool Settled(bool owner, int frame)
		{
			if (Answered && Granted && owner)
			{
				return frame > Frame + 1;
			}
			return false;
		}
	}
	internal static class BackpackLayout
	{
		internal static InventoryElement[] Elements(InventoryGrid grid)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			RectTransform rect = (RectTransform)((Component)grid).transform;
			int height;
			bool knownHeight = ExtraSlotsBridge.TryBackpackHeight(out height);
			int width = ((Humanoid)Player.m_localPlayer).GetInventory().GetWidth();
			return GameApi.Get<List<InventoryElement>>(grid, "m_elements").Where(delegate(InventoryElement e)
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_003d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0095: Unknown result type (might be due to invalid IL or missing references)
				//IL_0058: Unknown result type (might be due to invalid IL or missing references)
				//IL_005d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0072: Unknown result type (might be due to invalid IL or missing references)
				//IL_0077: Unknown result type (might be due to invalid IL or missing references)
				//IL_007a: Unknown result type (might be due to invalid IL or missing references)
				//IL_007f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0084: Unknown result type (might be due to invalid IL or missing references)
				//IL_0089: Unknown result type (might be due to invalid IL or missing references)
				if (Object.op_Implicit((Object)(object)e) && ((Component)e).gameObject.activeInHierarchy && e.Position.x >= 0 && e.Position.x < width && e.Position.y >= 0)
				{
					if (!knownHeight)
					{
						Rect rect2 = rect.rect;
						RectTransform obj = rect;
						RectTransform elementRectTransform = e.GetElementRectTransform();
						Rect rect3 = e.GetElementRectTransform().rect;
						return ((Rect)(ref rect2)).Contains(((Transform)obj).InverseTransformPoint(((Transform)elementRectTransform).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect3)).center))));
					}
					return e.Position.y < height;
				}
				return false;
			}).ToArray();
		}
	}
	internal static class ChestAccess
	{
		private static readonly Dictionary<Container, OpenTicket> tickets = new Dictionary<Container, OpenTicket>();

		private static readonly Dictionary<Container, float> rejected = new Dictionary<Container, float>();

		internal static Container Open
		{
			get
			{
				if (!Object.op_Implicit((Object)(object)InventoryGui.instance))
				{
					return null;
				}
				return GameApi.Get<Container>(InventoryGui.instance, "m_currentContainer");
			}
		}

		internal static bool Dragging
		{
			get
			{
				if (Object.op_Implicit((Object)(object)InventoryGui.instance))
				{
					return GameApi.Get<ItemData>(InventoryGui.instance, "m_dragItem") != null;
				}
				return false;
			}
		}

		internal static bool Allowed(Container c)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)c) || !Object.op_Implicit((Object)(object)localPlayer) || !((Character)localPlayer).IsOwner() || ((Character)localPlayer).IsDead() || !((Behaviour)c).isActiveAndEnabled || c.GetInventory() == null || !GameApi.Nearby((Component)(object)c, ((Component)localPlayer).transform.position))
			{
				return false;
			}
			ZNetView val = GameApi.View((Component)(object)c);
			if (Object.op_Implicit((Object)(object)val) && val.IsValid() && (bool)GameApi.Call(c, "CheckAccess", localPlayer.GetPlayerID()))
			{
				return PrivateArea.CheckAccess(((Component)c).transform.position, 0f, false, false);
			}
			return false;
		}

		internal static IEnumerable<Container> Near(Vector3 center)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			return Plugin.Chests.Where((Container c) => Object.op_Implicit((Object)(object)c) && GameApi.Nearby((Component)(object)c, center) && Allowed(c)).OrderBy(delegate(Container c)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Component)c).transform.position - center;
				return ((Vector3)(ref val)).sqrMagnitude;
			});
		}

		internal static bool Editable(Container c)
		{
			if (Allowed(c) && (Object)(object)Open == (Object)(object)c && c.IsInUse() && GameApi.Owned((Component)(object)c))
			{
				return !Dragging;
			}
			return false;
		}

		internal static bool Ready(Container c)
		{
			if (!Allowed(c))
			{
				return false;
			}
			ZNetView val = GameApi.View((Component)(object)c);
			bool flag = val.IsOwner();
			if (AccessPolicy.Busy(flag, c.IsInUse(), val.GetZDO().GetInt(ZDOVars.s_inUse, 0) != 0, (Object)(object)Open == (Object)(object)c, Object.op_Implicit((Object)(object)c.m_wagon) && c.m_wagon.InUse()))
			{
				return false;
			}
			if (tickets.TryGetValue(c, out var value))
			{
				if (!value.Settled(flag, Time.frameCount))
				{
					return false;
				}
				if (!c.IsInUse())
				{
					GameApi.Call(c, "Load");
				}
				tickets.Remove(c);
			}
			if (flag)
			{
				if (!c.IsInUse())
				{
					GameApi.Call(c, "Load");
				}
				return !GameApi.Get<bool>(c, "m_loading");
			}
			if (rejected.TryGetValue(c, out var value2) && Time.unscaledTime < value2)
			{
				return false;
			}
			if (tickets.Values.Count((OpenTicket t) => !t.Answered) >= 8 || val.GetZDO().GetOwner() == 0L)
			{
				return false;
			}
			tickets[c] = new OpenTicket(val.GetZDO().GetOwner(), Time.unscaledTime);
			val.InvokeRPC("RPC_RequestOpen", new object[1] { Player.m_localPlayer.GetPlayerID() });
			return false;
		}

		internal static bool Reply(Container c, long peer, bool granted)
		{
			if (!tickets.TryGetValue(c, out var value))
			{
				return true;
			}
			value.Accept(peer, granted, Time.frameCount);
			if (value.Answered && !value.Granted)
			{
				tickets.Remove(c);
				rejected[c] = Time.unscaledTime + 2f;
			}
			return false;
		}

		internal static bool Manual(Container c)
		{
			if (tickets.TryGetValue(c, out var value))
			{
				if (!value.Answered)
				{
					Plugin.Say("Waiting for this chest's owner. Try again shortly.");
					return false;
				}
				tickets.Remove(c);
			}
			rejected.Remove(c);
			return true;
		}

		internal static void Tick()
		{
			Container[] array = tickets.Keys.ToArray();
			foreach (Container val in array)
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					tickets.Remove(val);
					continue;
				}
				OpenTicket openTicket = tickets[val];
				if (openTicket.Settled(GameApi.Owned((Component)(object)val), Time.frameCount))
				{
					Ready(val);
				}
				if (!openTicket.Answered && Time.unscaledTime - openTicket.Started > 8f)
				{
					openTicket.Answered = true;
					openTicket.Granted = false;
				}
			}
		}

		internal static string State(Container c)
		{
			if (!Allowed(c))
			{
				return "access or range";
			}
			if (tickets.TryGetValue(c, out var value))
			{
				if (!value.Answered)
				{
					return "awaiting owner reply";
				}
				if (!value.Granted)
				{
					return "request declined/timed out; open chest once";
				}
				return "ownership packet pending";
			}
			if (c.IsInUse() && (Object)(object)Open != (Object)(object)c)
			{
				return "another open session";
			}
			if (!GameApi.Owned((Component)(object)c))
			{
				return "remote owner";
			}
			return "local owner";
		}
	}
	[HarmonyPatch(typeof(Container), "RPC_OpenResponse")]
	internal static class BackgroundChestReply
	{
		private static bool Prefix(Container __instance, long uid, bool granted)
		{
			return ChestAccess.Reply(__instance, uid, granted);
		}
	}
	[HarmonyPatch(typeof(Container), "Interact")]
	internal static class ManualChestOpen
	{
		private static bool Prefix(Container __instance, ref bool __result)
		{
			if (ChestAccess.Manual(__instance))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	internal static class ChestPresentation
	{
		internal static string FaceName(int face)
		{
			return (new string[4] { "Back", "Right", "Front", "Left" })[(face % 4 + 4) % 4];
		}

		internal static string Memory(bool enabled, IEnumerable<string> contents)
		{
			return "Remember: " + (enabled ? "On" : "Off") + " (" + contents.Where((string s) => !string.IsNullOrEmpty(s)).Distinct().Count() + ")";
		}
	}
	internal static class ChestUi
	{
		private static GameObject toolbar;

		private static GameObject panel;

		private static GameObject playerSort;

		private static Container selected;

		private static ChestSettings draft;

		private static TMP_InputField label;

		private static TMP_InputField priority;

		private static TMP_InputField search;

		private static GameObject searchBar;

		private static Player searchPlayer;

		private static readonly Vector3[] searchCorners = (Vector3[])(object)new Vector3[4];

		private static RectTransform inventoryBackground;

		private static RectTransform shiftedContainer;

		private static Vector2 backgroundBase;

		private static Vector2 backgroundApplied;

		private static Vector2 containerBase;

		private static Vector2 containerApplied;

		private static bool backgroundAdjusted;

		private static bool containerAdjusted;

		private static TMP_Text rememberText;

		private static TMP_Text faceText;

		private static readonly TextEntryState textEntry = new TextEntryState();

		internal static bool Editing
		{
			get
			{
				if (Object.op_Implicit((Object)(object)panel))
				{
					return panel.activeSelf;
				}
				return false;
			}
		}

		private static bool Typing
		{
			get
			{
				if (!Editing || ((!Object.op_Implicit((Object)(object)label) || !label.isFocused) && (!Object.op_Implicit((Object)(object)priority) || !priority.isFocused)))
				{
					if (InventoryGui.IsVisible() && Object.op_Implicit((Object)(object)search))
					{
						return search.isFocused;
					}
					return false;
				}
				return true;
			}
		}

		internal static bool BlockGameplayInput => textEntry.Blocks(Typing, Time.frameCount);

		internal static TMP_FontAsset Font
		{
			get
			{
				if (!Object.op_Implicit((Object)(object)InventoryGui.instance) || !Object.op_Implicit((Object)(object)InventoryGui.instance.m_playerName.font))
				{
					return TMP_Settings.defaultFontAsset;
				}
				return InventoryGui.instance.m_playerName.font;
			}
		}

		private static RectTransform Rect(Transform parent, string name, float x, float y, float w, float h)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.SetActive(false);
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(parent, false);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0f, 1f);
			val2.anchorMax = val3;
			val2.anchorMin = val3;
			val2.pivot = new Vector2(0f, 1f);
			val2.anchoredPosition = new Vector2(x, 0f - y);
			val2.sizeDelta = new Vector2(w, h);
			return val2;
		}

		private static TMP_Text Text(Transform parent, string text, float x, float y, float w, float h, int size = 18)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Rect(parent, "Text", x, y, w, h);
			TextMeshProUGUI obj = ((Component)val).gameObject.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj).font = Font;
			((TMP_Text)obj).fontSize = size;
			((TMP_Text)obj).text = text;
			((Graphic)obj).color = new Color(1f, 0.92f, 0.75f);
			((Graphic)obj).raycastTarget = false;
			((TMP_Text)obj).alignment = (TextAlignmentOptions)4097;
			((Component)val).gameObject.SetActive(true);
			return (TMP_Text)(object)obj;
		}

		private static Button Button(Transform parent, string name, float x, float y, float w, Action action)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			RectTransform val = Rect(parent, name, x, y, w, 34f);
			Image val2 = ((Component)val).gameObject.AddComponent<Image>();
			((Graphic)val2).color = new Color(0.22f, 0.18f, 0.13f, 0.98f);
			Button val3 = ((Component)val).gameObject.AddComponent<Button>();
			((Selectable)val3).targetGraphic = (Graphic)(object)val2;
			TMP_Text val4 = Text((Transform)(object)val, name, 8f, 0f, w - 16f, 34f);
			val4.alignment = (TextAlignmentOptions)514;
			UiSkin.Button(val3, val4);
			((Graphic)val2).raycastTarget = true;
			((UnityEvent)val3.onClick).AddListener((UnityAction)delegate
			{
				try
				{
					action();
				}
				catch (Exception ex)
				{
					Plugin.Log(ex.ToString());
					Plugin.Say("Operation failed; see client log.");
				}
			});
			((Component)val).gameObject.SetActive(true);
			return val3;
		}

		private static TMP_InputField InputBox(Transform parent, string text, float y, float x = 170f, float width = 265f)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Rect(parent, "Input", x, y, width, 34f);
			((Graphic)((Component)val).gameObject.AddComponent<Image>()).color = new Color(0.07f, 0.07f, 0.07f, 0.98f);
			RectTransform val2 = Rect((Transform)(object)val, "Text Area", 8f, 0f, width - 16f, 34f);
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.pivot = new Vector2(0.5f, 0.5f);
			val2.offsetMin = new Vector2(8f, 0f);
			val2.offsetMax = new Vector2(-8f, 0f);
			((Component)val2).gameObject.AddComponent<RectMask2D>();
			((Component)val2).gameObject.SetActive(true);
			TMP_Text val3 = Text((Transform)(object)val2, "", 0f, 0f, width - 16f, 34f);
			((Graphic)val3).color = Color.white;
			val3.richText = false;
			val3.rectTransform.anchorMin = Vector2.zero;
			val3.rectTransform.anchorMax = Vector2.one;
			val3.rectTransform.pivot = new Vector2(0.5f, 0.5f);
			RectTransform rectTransform = val3.rectTransform;
			Vector2 offsetMin = (val3.rectTransform.offsetMax = Vector2.zero);
			rectTransform.offsetMin = offsetMin;
			val3.alignment = (TextAlignmentOptions)513;
			val3.textWrappingMode = (TextWrappingModes)0;
			TMP_InputField obj = ((Component)val).gameObject.AddComponent<TMP_InputField>();
			obj.textViewport = val2;
			obj.textComponent = val3;
			obj.lineType = (LineType)0;
			((UnityEvent<string>)(object)obj.onSelect).AddListener((UnityAction<string>)delegate
			{
				textEntry.FocusChanged(Time.frameCount);
			});
			((UnityEvent<string>)(object)obj.onDeselect).AddListener((UnityAction<string>)delegate
			{
				textEntry.FocusChanged(Time.frameCount);
			});
			((UnityEvent<string>)(object)obj.onEndEdit).AddListener((UnityAction<string>)delegate
			{
				textEntry.FocusChanged(Time.frameCount);
			});
			obj.characterLimit = 96;
			obj.text = text;
			((Component)val).gameObject.SetActive(true);
			return obj;
		}

		internal static void Tick()
		{
			//IL_009a: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0361: Unknown result type (might be due to invalid IL or missing references)
			//IL_0362: Unknown result type (might be due to invalid IL or missing references)
			//IL_0374: Unknown result type (might be due to invalid IL or missing references)
			//IL_037a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0380: Unknown result type (might be due to invalid IL or missing references)
			//IL_038b: Unknown result type (might be due to invalid IL or missing references)
			//IL_039f: Unknown result type (might be due to invalid IL or missing references)
			InventoryGui instance = InventoryGui.instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				return;
			}
			if ((Object)(object)searchPlayer != (Object)(object)Player.m_localPlayer)
			{
				searchPlayer = Player.m_localPlayer;
				StorageSearch.Query = "";
				StorageSearch.Clear();
				if (Object.op_Implicit((Object)(object)search))
				{
					search.SetTextWithoutNotify("");
				}
			}
			Vector2 val = default(Vector2);
			if (!Object.op_Implicit((Object)(object)searchBar))
			{
				RectTransform obj = Rect((Transform)(object)instance.m_player, "WorkshopWorkerStorageSearch", 12f, 0f, 420f, 38f);
				((Vector2)(ref val))..ctor(0f, 0f);
				obj.anchorMax = val;
				obj.anchorMin = val;
				obj.anchoredPosition = new Vector2(12f, -8f);
				searchBar = ((Component)obj).gameObject;
				Text((Transform)(object)obj, "Find in Storage", 0f, 0f, 110f, 34f, 16).textWrappingMode = (TextWrappingModes)0;
				search = InputBox((Transform)(object)obj, "", 0f, 120f, 240f);
				((UnityEvent<string>)(object)search.onValueChanged).AddListener((UnityAction<string>)delegate(string value)
				{
					StorageSearch.Query = value;
				});
				Button((Transform)(object)obj, "Clear", 368f, 0f, 65f, delegate
				{
					search.text = "";
					StorageSearch.Clear();
				});
			}
			searchBar.SetActive(InventoryGui.IsVisible());
			if (Object.op_Implicit((Object)(object)search) && search.isFocused && Input.GetKeyDown((KeyCode)27))
			{
				search.DeactivateInputField(false);
			}
			if (!Object.op_Implicit((Object)(object)toolbar))
			{
				RectTransform obj2 = Rect((Transform)(object)instance.m_container, "WorkshopWorkerTools", 0f, 0f, 310f, 38f);
				((Vector2)(ref val))..ctor(0f, 0f);
				obj2.anchorMax = val;
				obj2.anchorMin = val;
				obj2.anchoredPosition = new Vector2(12f, -6f);
				toolbar = ((Component)obj2).gameObject;
				Button((Transform)(object)obj2, "Chest Rules", 0f, 0f, 190f, Open);
				Button((Transform)(object)obj2, "Sort", 198f, 0f, 90f, delegate
				{
					Storage.Sort(ChestAccess.Open);
				});
			}
			toolbar.SetActive(InventoryGui.IsVisible() && Object.op_Implicit((Object)(object)ChestAccess.Open));
			if (!Object.op_Implicit((Object)(object)playerSort) && Object.op_Implicit((Object)(object)instance.m_weight))
			{
				Image val2 = ((IEnumerable<Image>)((Component)instance.m_weight.transform.parent).GetComponentsInChildren<Image>(true)).FirstOrDefault((Func<Image, bool>)((Image i) => Object.op_Implicit((Object)(object)i.sprite) && ((Object)i).name.IndexOf("weight", StringComparison.OrdinalIgnoreCase) >= 0));
				Button obj3 = Button((Transform)(object)(Object.op_Implicit((Object)(object)val2) ? ((Graphic)val2).rectTransform : instance.m_weight.rectTransform), "Sort", 0f, 0f, 90f, Storage.SortPlayer);
				RectTransform val3 = (RectTransform)((Component)obj3).transform;
				((Vector2)(ref val))..ctor(0.5f, 0f);
				val3.anchorMax = val;
				val3.anchorMin = val;
				val3.pivot = new Vector2(0.5f, 1f);
				val3.anchoredPosition = new Vector2(0f, -8f);
				playerSort = ((Component)obj3).gameObject;
			}
			if (Object.op_Implicit((Object)(object)playerSort))
			{
				playerSort.SetActive(InventoryGui.IsVisible());
			}
			if (Editing && (!InventoryGui.IsVisible() || (Object)(object)ChestAccess.Open != (Object)(object)selected || Input.GetKeyDown((KeyCode)27)))
			{
				Close();
			}
		}

		private static void RestoreSearchLayout()
		{
			//IL_0018: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			if (backgroundAdjusted && Object.op_Implicit((Object)(object)inventoryBackground) && inventoryBackground.offsetMin == backgroundApplied)
			{
				inventoryBackground.offsetMin = backgroundBase;
			}
			if (containerAdjusted && Object.op_Implicit((Object)(object)shiftedContainer) && shiftedContainer.anchoredPosition == containerApplied)
			{
				shiftedContainer.anchoredPosition = containerBase;
			}
			backgroundAdjusted = (containerAdjusted = false);
		}

		internal static void PositionSearch()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			RestoreSearchLayout();
			InventoryGui instance = InventoryGui.instance;
			if (!Object.op_Implicit((Object)(object)searchBar) || !Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !InventoryGui.IsVisible())
			{
				return;
			}
			Transform obj = ((Transform)instance.m_player).Find("Bkg");
			inventoryBackground = (RectTransform)(object)((obj is RectTransform) ? obj : null);
			Rect rect = instance.m_player.rect;
			float num = ((Rect)(ref rect)).yMin;
			if (Object.op_Implicit((Object)(object)inventoryBackground))
			{
				inventoryBackground.GetWorldCorners(searchCorners);
				num = Mathf.Min(num, ((Transform)instance.m_player).InverseTransformPoint(searchCorners[0]).y);
			}
			InventoryElement[] array = BackpackLayout.Elements(instance.m_playerGrid);
			for (int i = 0; i < array.Length; i++)
			{
				array[i].GetElementRectTransform().GetWorldCorners(searchCorners);
				Vector3[] array2 = searchCorners;
				foreach (Vector3 val in array2)
				{
					num = Mathf.Min(num, ((Transform)instance.m_player).InverseTransformPoint(val).y);
				}
			}
			float num2 = num - 0f - 34f - 12f;
			RectTransform val2 = (RectTransform)searchBar.transform;
			float num3 = num;
			rect = instance.m_player.rect;
			val2.anchoredPosition = new Vector2(12f, num3 - ((Rect)(ref rect)).yMin - 0f);
			if (Object.op_Implicit((Object)(object)inventoryBackground))
			{
				inventoryBackground.GetWorldCorners(searchCorners);
				Transform parent = ((Transform)inventoryBackground).parent;
				float y = parent.InverseTransformPoint(searchCorners[0]).y;
				float y2 = parent.InverseTransformPoint(((Transform)instance.m_player).TransformPoint(new Vector3(0f, num2, 0f))).y;
				backgroundBase = inventoryBackground.offsetMin;
				backgroundApplied = backgroundBase + new Vector2(0f, Mathf.Min(0f, y2 - y));
				inventoryBackground.offsetMin = backgroundApplied;
				backgroundAdjusted = true;
			}
			if (Object.op_Implicit((Object)(object)instance.m_container) && ((Component)instance.m_container).gameObject.activeInHierarchy)
			{
				shiftedContainer = instance.m_container;
				Transform obj2 = ((Transform)shiftedContainer).Find("Bkg");
				RectTransform val3 = (RectTransform)(object)((obj2 is RectTransform) ? obj2 : null);
				(Object.op_Implicit((Object)(object)val3) ? val3 : shiftedContainer).GetWorldCorners(searchCorners);
				Transform parent2 = ((Transform)shiftedContainer).parent;
				float y3 = parent2.InverseTransformPoint(searchCorners[1]).y;
				float y4 = parent2.InverseTransformPoint(((Transform)instance.m_player).TransformPoint(new Vector3(0f, num2 - 10f, 0f))).y;
				containerBase = shiftedContainer.anchoredPosition;
				containerApplied = containerBase + new Vector2(0f, Mathf.Min(0f, y4 - y3));
				shiftedContainer.anchoredPosition = containerApplied;
				containerAdjusted = true;
			}
		}

		private static void Open()
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: 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_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			selected = ChestAccess.Open;
			if (!ChestAccess.Editable(selected))
			{
				Plugin.Say("Chest access changed. Reopen the chest.");
				Plugin.Log("Rules editor: " + ChestAccess.State(selected));
				return;
			}
			if (Object.op_Implicit((Object)(object)panel))
			{
				Object.Destroy((Object)(object)panel);
			}
			draft = ChestSettings.Read(selected);
			draft.Learn(selected);
			RectTransform val = Rect(((Component)((Component)InventoryGui.instance).GetComponentInParent<Canvas>()).transform, "WorkshopWorkerRules", 0f, 0f, 460f, 372f);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 0.5f);
			val.pivot = val2;
			Vector2 anchorMin = (val.anchorMax = val2);
			val.anchorMin = anchorMin;
			val.anchoredPosition = Vector2.zero;
			((Graphic)((Component)val).gameObject.AddComponent<Image>()).color = new Color(0.09f, 0.085f, 0.075f, 0.99f);
			panel = ((Component)val).gameObject;
			Text((Transform)(object)val, "Workshop Worker · Chest rules", 20f, 12f, 420f, 40f, 23);
			Text((Transform)(object)val, "Label", 20f, 76f, 65f, 34f);
			label = InputBox((Transform)(object)val, draft.label, 76f, 95f, 340f);
			(string, string)[] array = new(string, string)[7]
			{
				("White", "#FFFFFF"),
				("Red", "#FF6565"),
				("Orange", "#FFAA44"),
				("Yellow", "#FFEE66"),
				("Green", "#77DD77"),
				("Blue", "#66BBFF"),
				("Purple", "#CC99FF")
			};
			Color color = default(Color);
			for (int i = 0; i < array.Length; i++)
			{
				(string, string) choice = array[i];
				TMP_Text componentInChildren = ((Component)Button((Transform)(object)val, choice.Item1, 20 + i * 60, 120f, 56f, delegate
				{
					//IL_0032: Unknown result type (might be due to invalid IL or missing references)
					draft.color = choice.Item2;
					Color color3 = default(Color);
					ColorUtility.TryParseHtmlString(choice.Item2, ref color3);
					((Graphic)label.textComponent).color = color3;
				})).GetComponentInChildren<TMP_Text>();
				ColorUtility.TryParseHtmlString(choice.Item2, ref color);
				((Graphic)componentInChildren).color = color;
				componentInChildren.fontSizeMax = 14f;
				componentInChildren.fontSizeMin = 10f;
			}
			Color color2 = default(Color);
			if (ColorUtility.TryParseHtmlString(draft.color, ref color2))
			{
				((Graphic)label.textComponent).color = color2;
			}
			Text((Transform)(object)val, "Priority (0 first)", 20f, 170f, 145f, 34f);
			priority = InputBox((Transform)(object)val, draft.priority.ToString(), 170f);
			faceText = ((Component)Button((Transform)(object)val, "", 20f, 218f, 200f, delegate
			{
				draft.face = (draft.face + 1) % 4;
				UpdateButtons();
			})).GetComponentInChildren<TMP_Text>();
			rememberText = ((Component)Button((Transform)(object)val, "", 235f, 218f, 200f, delegate
			{
				draft.remember = !draft.remember;
				draft.Learn(selected);
				UpdateButtons();
			})).GetComponentInChildren<TMP_Text>();
			Button((Transform)(object)val, "Remember current contents", 20f, 264f, 265f, delegate
			{
				draft.remember = true;
				draft.Learn(selected);
				UpdateButtons();
			});
			Button((Transform)(object)val, "Forget types", 295f, 264f, 140f, delegate
			{
				draft.contents.Clear();
				draft.remember = false;
				UpdateButtons();
			});
			Button((Transform)(object)val, "Save", 235f, 322f, 95f, Save);
			Button((Transform)(object)val, "Cancel", 340f, 322f, 95f, Close);
			UpdateButtons();
			((Component)val).gameObject.SetActive(true);
		}

		private static void UpdateButtons()
		{
			faceText.text = "Label: " + ChestPresentation.FaceName(draft.face);
			rememberText.text = ChestPresentation.Memory(draft.remember, draft.contents);
		}

		private static void Save()
		{
			if (!ChestAccess.Editable(selected))
			{
				Plugin.Say("Chest access changed; settings were not saved.");
				return;
			}
			if (!int.TryParse(priority.text, out var result) || result < 0 || result > 9)
			{
				Plugin.Say("Use a priority from 0 to 9.");
				return;
			}
			draft.label = label.text.Trim();
			draft.priority = result;
			draft.Learn(selected);
			draft.Save(selected);
			Close();
			RefreshLabels();
		}

		private static void Close()
		{
			if (Typing)
			{
				textEntry.FocusChanged(Time.frameCount);
			}
			if (Object.op_Implicit((Object)(object)panel))
			{
				Object.Destroy((Object)(object)panel);
			}
			panel = null;
			selected = null;
		}

		internal static void Dispose()
		{
			Close();
			if (Object.op_Implicit((Object)(object)toolbar))
			{
				Object.Destroy((Object)(object)toolbar);
			}
			if (Object.op_Implicit((Object)(object)playerSort))
			{
				Object.Destroy((Object)(object)playerSort);
			}
			if (Object.op_Implicit((Object)(object)searchBar))
			{
				Object.Destroy((Object)(object)searchBar);
			}
			RestoreSearchLayout();
			StorageSearchDim.Dispose();
			StorageSearch.Query = "";
			StorageSearch.Clear();
		}

		internal static void RefreshLabels()
		{
			SurfaceLabels.Refresh();
		}
	}
	[HarmonyPatch(typeof(Container), "Save")]
	internal static class RememberChestContents
	{
		private static void Postfix(Container __instance)
		{
			if (!GameApi.Owned((Component)(object)__instance))
			{
				return;
			}
			ChestSettings chestSettings = ChestSettings.Read(__instance);
			if (chestSettings.remember)
			{
				int count = chestSettings.contents.Count;
				chestSettings.Learn(__instance);
				if (count != chestSettings.contents.Count)
				{
					chestSettings.Save(__instance);
				}
			}
		}
	}
	internal static class Crafting
	{
		internal static int QueryDepth;

		internal static int SpendDepth;

		internal static Container[] Held;

		internal static bool PlayerInventory(Inventory inv)
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return inv == ((Humanoid)Player.m_localPlayer).GetInventory();
			}
			return false;
		}

		internal static IEnumerable<Container> Sources()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			return (Held ?? ChestAccess.Near(((Component)Player.m_localPlayer).transform.position).ToArray()).Where(ChestAccess.Ready);
		}

		internal static bool Match(ItemData i, string name, int quality, bool world)
		{
			if (i.m_shared.m_name == name && (quality < 0 || i.m_quality == quality))
			{
				if (world)
				{
					return i.m_worldLevel >= Game.m_worldLevel;
				}
				return true;
			}
			return false;
		}

		internal static int Count(string name, int quality, bool world)
		{
			return Sources().Sum((Container c) => (from i in c.GetInventory().GetAllItems()
				where Match(i, name, quality, world)
				select i).Sum((ItemData i) => i.m_stack));
		}

		internal static ItemData Find(string name, int quality, bool prefab)
		{
			return Sources().SelectMany((Container c) => c.GetInventory().GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => (prefab ? Storage.ItemId(i) : i.m_shared.m_name) == name && (quality < 0 || quality == i.m_quality) && i.m_worldLevel >= Game.m_worldLevel));
		}

		internal static Container Source(ItemData item)
		{
			return Sources().FirstOrDefault((Func<Container, bool>)((Container c) => c.GetInventory().GetAllItems().Contains(item)));
		}

		internal static void BeginSpend()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (SpendDepth++ == 0)
			{
				Held = ChestAccess.Near(((Component)Player.m_localPlayer).transform.position).Where(ChestAccess.Ready).ToArray();
			}
		}

		internal static void EndSpend()
		{
			if (--SpendDepth == 0)
			{
				Held = null;
			}
		}
	}
	[HarmonyPatch]
	internal static class RecipeQueries
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(Player), "HaveRequirements", new Type[4]
			{
				typeof(Recipe),
				typeof(bool),
				typeof(int),
				typeof(int)
			}, (Type[])null);
			yield return AccessTools.Method(typeof(Player), "HaveRequirements", new Type[2]
			{
				typeof(Piece),
				typeof(RequirementMode)
			}, (Type[])null);
			yield return AccessTools.Method(typeof(Player), "GetFirstRequiredItem", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(InventoryGui), "SetupRequirement", (Type[])null, (Type[])null);
		}

		private static void Prefix(out bool __state)
		{
			__state = Plugin.Craft.Value && Object.op_Implicit((Object)(object)Player.m_localPlayer);
			if (__state)
			{
				Crafting.QueryDepth++;
			}
		}

		private static Exception Finalizer(Exception __exception, bool __state)
		{
			if (__state)
			{
				Crafting.QueryDepth--;
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Inventory), "CountItems")]
	internal static class NearbyCounts
	{
		private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result)
		{
			if (Crafting.QueryDepth > 0 && name != null && Crafting.PlayerInventory(__instance))
			{
				__result += Crafting.Count(name, quality, matchWorldLevel);
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "GetItem", new Type[]
	{
		typeof(string),
		typeof(int),
		typeof(bool)
	})]
	internal static class NearbyItem
	{
		private static void Postfix(Inventory __instance, string name, int quality, bool isPrefabName, ref ItemData __result)
		{
			if (__result == null && Crafting.QueryDepth > 0 && Crafting.PlayerInventory(__instance))
			{
				__result = Crafting.Find(name, quality, isPrefabName);
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "HaveItem", new Type[]
	{
		typeof(string),
		typeof(bool)
	})]
	internal static class NearbyHaveItem
	{
		private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result)
		{
			if (!__result && Crafting.QueryDepth > 0 && Crafting.PlayerInventory(__instance))
			{
				__result = Crafting.Count(name, -1, matchWorldLevel) > 0;
			}
		}
	}
	[HarmonyPatch]
	internal static class RecipeSpend
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(InventoryGui), "DoCrafting", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Player), "ConsumeResources", (Type[])null, (Type[])null);
		}

		private static void Prefix(out bool __state)
		{
			__state = Plugin.Craft.Value && Object.op_Implicit((Object)(object)Player.m_localPlayer);
			if (__state)
			{
				Crafting.BeginSpend();
			}
		}

		private static Exception Finalizer(Exception __exception, bool __state)
		{
			if (__state)
			{
				Crafting.EndSpend();
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
	{
		typeof(string),
		typeof(int),
		typeof(int),
		typeof(bool)
	})]
	internal static class ConsumeNearbyByName
	{
		private static bool Prefix(Inventory __instance, string name, int amount, int itemQuality, bool worldLevelBased)
		{
			if (Crafting.SpendDepth == 0 || !Crafting.PlayerInventory(__instance))
			{
				return true;
			}
			(Inventory inv, ItemData item)[] array = ((IEnumerable<Inventory>)(object)new Inventory[1] { __instance }).Concat(from c in Crafting.Sources()
				select c.GetInventory()).ToArray().SelectMany((Inventory inv) => from i in inv.GetAllItems()
				where Crafting.Match(i, name, itemQuality, worldLevelBased)
				select (inv: inv, item: i))
				.ToArray();
			if (array.Sum(((Inventory inv, ItemData item) p) => p.item.m_stack) < amount)
			{
				throw new InvalidOperationException("Crafting source changed before ingredient consumption.");
			}
			(Inventory, ItemData)[] array2 = array;
			for (int num = 0; num < array2.Length; num++)
			{
				(Inventory, ItemData) tuple = array2[num];
				int num2 = Math.Min(amount, tuple.Item2.m_stack);
				if (num2 <= 0)
				{
					break;
				}
				tuple.Item1.RemoveItem(tuple.Item2, num2);
				amount -= num2;
			}
			return false;
		}
	}
	[HarmonyPatch]
	internal static class ManualMachineScope
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			string[] array = new string[2] { "OnAddOre", "OnAddFuel" };
			foreach (string text in array)
			{
				yield return AccessTools.Method(typeof(Smelter), text, (Type[])null, (Type[])null);
			}
			array = new string[3] { "OnInteract", "OnUseItem", "OnAddFuelSwitch" };
			foreach (string text2 in array)
			{
				yield return AccessTools.Method(typeof(CookingStation), text2, (Type[])null, (Type[])null);
			}
			array = new string[2] { "Interact", "UseItem" };
			foreach (string m in array)
			{
				yield return AccessTools.Method(typeof(Fermenter), m, (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(Fireplace), m, (Type[])null, (Type[])null);
			}
		}

		private static void Prefix(Component __instance, out bool __state)
		{
			//IL_0024: 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)
			__state = Plugin.Craft.Value && Object.op_Implicit((Object)(object)Player.m_localPlayer) && GameApi.Nearby(__instance, ((Component)Player.m_localPlayer).transform.position) && PrivateArea.CheckAccess(__instance.transform.position, 0f, false, false);
			if (__state)
			{
				Crafting.BeginSpend();
				Crafting.QueryDepth++;
			}
		}

		private static Exception Finalizer(Exception __exception, bool __state)
		{
			if (__state)
			{
				Crafting.QueryDepth--;
				Crafting.EndSpend();
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Inventory), "RemoveOneItem")]
	internal static class ConsumeNearbyOne
	{
		private static bool Prefix(Inventory __instance, ItemData item, ref bool __result)
		{
			if (Crafting.SpendDepth == 0 || !Crafting.PlayerInventory(__instance) || __instance.ContainsItem(item))
			{
				return true;
			}
			Container val = Crafting.Source(item);
			__result = Object.op_Implicit((Object)(object)val) && val.GetInventory().RemoveOneItem(item);
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
	{
		typeof(ItemData),
		typeof(int)
	})]
	internal static class ConsumeNearbyStack
	{
		private static bool Prefix(Inventory __instance, ItemData item, int amount, ref bool __result)
		{
			if (Crafting.SpendDepth == 0 || !Crafting.PlayerInventory(__instance) || __instance.ContainsItem(item))
			{
				return true;
			}
			Container val = Crafting.Source(item);
			__result = Object.op_Implicit((Object)(object)val) && val.GetInventory().RemoveItem(item, amount);
			return false;
		}
	}
	[HarmonyPatch]
	internal static class MachineIngredientPreview
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(Smelter), "OnHoverAddOre", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Smelter), "OnHoverAddFuel", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(CookingStation), "GetHoverText", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(CookingStation), "OnHoverFuelSwitch", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Fermenter), "GetHoverText", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Fireplace), "GetHoverText", (Type[])null, (Type[])null);
		}

		private static void Postfix(Component __instance, MethodBase __originalMethod, ref string __result)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Craft.Value || !Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return;
			}
			IEnumerable<string> enumerable = (from i in Production.Ingredients(__instance, __originalMethod.Name.IndexOf("Fuel", StringComparison.OrdinalIgnoreCase) >= 0 || __instance is Fireplace)
				select i.m_itemData.m_shared.m_name).Distinct();
			Container[] source = ChestAccess.Near(__instance.transform.position).Where(ChestAccess.Ready).ToArray();
			foreach (string name in enumerable)
			{
				int num = source.Sum((Container c) => c.GetInventory().CountItems(name, -1, true));
				if (num > 0)
				{
					__result = __result + "\n" + Localization.instance.Localize(name) + ": " + num + " in nearby storage";
				}
			}
		}
	}
	internal static class ExtraSlotsBridge
	{
		private static MethodInfo backpackHeight;

		private static bool heightResolved;

		internal static bool TryBackpackHeight(out int height)
		{
			height = 0;
			try
			{
				if (!heightResolved)
				{
					heightResolved = true;
					backpackHeight = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "ExtraSlots")?.GetType("ExtraSlots.API")?.GetMethod("GetInventoryHeightPlayer", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null);
				}
				if (backpackHeight == null)
				{
					return false;
				}
				height = (int)backpackHeight.Invoke(null, null);
				return height > 0;
			}
			catch
			{
				return false;
			}
		}

		internal static bool ReducedSlots(IEnumerable<Vector2i> visible, out Vector2i[] slots, out Func<Vector2i, bool> special)
		{
			slots = Array.Empty<Vector2i>();
			special = null;
			try
			{
				Assembly? assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "ExtraSlots");
				Type type = assembly?.GetType("ExtraSlots.API");
				Type type2 = assembly?.GetType("ExtraSlots.LightenedSlots");
				MethodInfo methodInfo = type?.GetMethod("IsGridPositionASlot", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Vector2i) }, null);
				MethodInfo methodInfo2 = type2?.GetMethod("IsRowAffected", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(int) }, null);
				PropertyInfo propertyInfo = type2?.GetProperty("WeightFactor", BindingFlags.Static | BindingFlags.Public);
				if (methodInfo == null || methodInfo2 == null || propertyInfo == null || (float)propertyInfo.GetValue(null) >= 1f)
				{
					return false;
				}
				Func<Vector2i, bool> check = (Func<Vector2i, bool>)Delegate.CreateDelegate(typeof(Func<Vector2i, bool>), methodInfo);
				Func<int, bool> reduced = (Func<int, bool>)Delegate.CreateDelegate(typeof(Func<int, bool>), methodInfo2);
				slots = visible.Where((Vector2i p) => p.y > 0 && !check(p) && reduced(p.y)).ToArray();
				special = check;
				return slots.Length != 0;
			}
			catch (Exception ex)
			{
				Plugin.Log("ExtraSlots weight-row detection unavailable: " + ex.Message);
				return false;
			}
		}
	}
	internal static class InventorySort
	{
		internal static int HeavySlots(Inventory inventory, Func<ItemData, bool> keep, IEnumerable<Vector2i> preferred, Func<ItemData, float> weight, Func<ItemData, string> name)
		{
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			ItemData[] array = (from i in inventory.GetAllItems()
				where !keep(i)
				select i).ToArray();
			HashSet<Vector2i> blocked = new HashSet<Vector2i>(from i in inventory.GetAllItems().Where(keep)
				select i.m_gridPos);
			Vector2i[] array2 = (from p in preferred.Where((Vector2i p) => !blocked.Contains(p)).Distinct()
				orderby p.y descending, p.x
				select p).Take(array.Length).ToArray();
			ItemData[] array3 = array.OrderByDescending(weight).ThenBy<ItemData, string>(name, StringComparer.CurrentCultureIgnoreCase).Take(array2.Length)
				.ToArray();
			Vector2i[] array4 = (from p in array.Select((ItemData i) => i.m_gridPos).Except(array2)
				orderby p.y, p.x
				select p).ToArray();
			ItemData[] array5 = array.Except(array3).OrderBy<ItemData, string>(name, StringComparer.CurrentCultureIgnoreCase).ThenByDescending((ItemData i) => i.m_quality)
				.ThenByDescending((ItemData i) => i.m_stack)
				.ToArray();
			for (int num = 0; num < array3.Length; num++)
			{
				array3[num].m_gridPos = array2[num];
			}
			for (int num2 = 0; num2 < array5.Length; num2++)
			{
				array5[num2].m_gridPos = array4[num2];
			}
			return array.Length;
		}

		internal static int Occupied(Inventory inventory, Func<ItemData, bool> keep, Func<ItemData, string> name)
		{
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			ItemData[] source = (from i in inventory.GetAllItems()
				where !keep(i)
				select i).ToArray();
			Vector2i[] array = (from i in source
				select i.m_gridPos into p
				orderby p.y, p.x
				select p).ToArray();
			ItemData[] array2 = source.OrderBy<ItemData, string>(name, StringComparer.CurrentCultureIgnoreCase).ThenByDescending((ItemData i) => i.m_quality).ThenByDescending((ItemData i) => i.m_stack)
				.ToArray();
			for (int num = 0; num < array2.Length; num++)
			{
				array2[num].m_gridPos = array[num];
			}
			return array2.Length;
		}
	}
	internal static class LiveObjectPool
	{
		internal static T Get<T>(List<T> items, int index, Func<T, bool> alive, Func<T> create) where T : class
		{
			while (items.Count <= index)
			{
				items.Add(null);
			}
			if (!alive(items[index]))
			{
				items[index] = create();
			}
			return items[index];
		}
	}
	[BepInPlugin("local.WorkshopWorker", "Workshop Worker", "1.0.2")]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal static Plugin Self;

		internal static ConfigEntry<float> Range;

		internal static ConfigEntry<bool> Hotbar;

		internal static ConfigEntry<bool> Craft;

		internal static ConfigEntry<bool> Automate;

		internal static ConfigEntry<KeyboardShortcut> StackKey;

		internal static ConfigEntry<KeyboardShortcut> ReserveKey;

		internal static ConfigEntry<KeyboardShortcut> InputKey;

		internal static ConfigEntry<KeyboardShortcut> OutputKey;

		internal static ConfigEntry<KeyCode> ClearKey;

		internal static ConfigEntry<bool> ShowVfx;

		internal static ConfigEntry<bool> UniformLabelSize;

		internal static ConfigEntry<bool> SortHeavyToReducedRows;

		internal static ConfigEntry<float> SearchGlowIntensity;

		internal static ConfigEntry<KeyboardShortcut> VfxKey;

		internal static ConfigEntry<KeyboardShortcut> RepairKey;

		private Harmony patches;

		private float scanAt;

		private float workAt;

		internal static Container[] Chests = Array.Empty<Container>();

		internal static Component[] Machines = Array.Empty<Component>();

		private void Awake()
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Expected O, but got Unknown
			if (Application.isBatchMode)
			{
				((Behaviour)this).enabled = false;
				return;
			}
			Self = this;
			Range = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RangeMeters", 75f, new ConfigDescription("Nearby loaded storage range shared by all features.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			Hotbar = ((BaseUnityPlugin)this).Config.Bind<bool>("Storage", "ProtectHotbar", true, "Exclude the first inventory row from quick stack.");
			SearchGlowIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("Find in Storage", "HighlightIntensity", 0.85f, new ConfigDescription("Blue glow strength on matching chests. Increase if hard to see; decrease if too bright. 0 keeps only the blue tint. Default is 0.85.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			SortHeavyToReducedRows = ((BaseUnityPlugin)this).Config.Bind<bool>("Inventory", "SortHeaviestStacksToWeightReducedRows", false, "Player Sort button: place the heaviest eligible stacks in ExtraSlots' active weight-reduced inventory rows, including empty slots. Protects hotbar, equipped, quest, reserved and special slots. Requires ExtraSlots with weight reduction enabled; otherwise normal sorting is used. False keeps alphabetical sorting.");
			UniformLabelSize = ((BaseUnityPlugin)this).Config.Bind<bool>("Chest Labels", "UseUniformTextSize", true, "Use the smallest fitted font size among currently loaded chest labels for every chest label. True: consistent text size. False: each label sizes itself independently. Recalculated as chests load or labels change; wrapping stays enabled.");
			Craft = ((BaseUnityPlugin)this).Config.Bind<bool>("Crafting", "Enabled", true, "Use accessible nearby chests for recipes and manual machine inputs.");
			Automate = ((BaseUnityPlugin)this).Config.Bind<bool>("Production", "Enabled", true, "Operate linked vanilla machines while this client owns and loads them.");
			StackKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keys", "QuickStack", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Stack unprotected inventory items into matching nearby storage.");
			ReserveKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keys", "ReserveSlot", new KeyboardShortcut((KeyCode)325, (KeyCode[])(object)new KeyCode[2]
			{
				(KeyCode)308,
				(KeyCode)304
			}), "Toggle protection on the hovered player inventory slot, including an empty slot.");
			InputKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keys", "LinkInputOrFuel", new KeyboardShortcut((KeyCode)323, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Select the machine input or fuel control, then a chest within 30 seconds.");
			OutputKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keys", "LinkOutput", new KeyboardShortcut((KeyCode)324, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Select a machine, then a chest within 30 seconds.");
			ClearKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keys", "RemoveLinkModifier", (KeyCode)304, "Hold along with a link gesture to remove the selected chest binding.");
			ShowVfx = ((BaseUnityPlugin)this).Config.Bind<bool>("Production", "ShowLinkVfx", true, "Show connection lines and the selected control highlight. Automation is unaffected.");
			VfxKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keys", "ToggleLinkVfx", new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()), "Toggle production connection visuals and save the setting.");
			RepairKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keys", "RepairAll", new KeyboardShortcut((KeyCode)59, Array.Empty<KeyCode>()), "Repair loaded building pieces within 100 metres, respecting wards.");
			patches = new Harmony("local.WorkshopWorker");
			patches.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Workshop Worker 1.0.2 by Daxtillion loaded; client only.");
		}

		private void Update()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !((Character)Player.m_localPlayer).IsOwner())
			{
				StorageSearch.Clear();
				return;
			}
			try
			{
				if (Time.unscaledTime >= scanAt)
				{
					scanAt = Time.unscaledTime + 2f;
					Chests = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
					Machines = Object.FindObjectsByType<Smelter>((FindObjectsSortMode)0).Cast<Component>().Concat((IEnumerable<Component>)(object)Object.FindObjectsByType<CookingStation>((FindObjectsSortMode)0))
						.Concat((IEnumerable<Component>)(object)Object.FindObjectsByType<Fermenter>((FindObjectsSortMode)0))
						.Concat((IEnumerable<Component>)(object)Object.FindObjectsByType<Fireplace>((FindObjectsSortMode)0))
						.ToArray();
					ChestUi.RefreshLabels();
				}
				ChestAccess.Tick();
				KeyboardShortcut value;
				if (!ChestUi.BlockGameplayInput)
				{
					value = RepairKey.Value;
					if (((KeyboardShortcut)(ref value)).IsDown() && (bool)GameApi.Call(Player.m_localPlayer, "TakeInput"))
					{
						Repairs.Begin();
					}
				}
				Repairs.Tick();
				Reservations.Tick();
				ChestUi.Tick();
				StorageSearch.Tick();
				if (!ChestUi.BlockGameplayInput && !ChestUi.Editing && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()))
				{
					value = StackKey.Value;
					if (((KeyboardShortcut)(ref value)).IsDown())
					{
						Storage.Begin();
					}
				}
				Storage.Tick();
				Production.Gestures();
				if (!ChestUi.BlockGameplayInput && !ChestUi.Editing && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()))
				{
					value = VfxKey.Value;
					if (((KeyboardShortcut)(ref value)).IsDown())
					{
						ShowVfx.Value = !ShowVfx.Value;
						((BaseUnityPlugin)this).Config.Save();
						Say("Connection visuals " + (ShowVfx.Value ? "on." : "off."));
					}
				}
				if (!(Time.unscaledTime >= workAt))
				{
					return;
				}
				workAt = Time.unscaledTime + 1f;
				if (Craft.Value && (InventoryGui.IsVisible() || Object.op_Implicit((Object)(object)Production.HoverMachine())))
				{
					foreach (Container item in ChestAccess.Near(((Component)Player.m_localPlayer).transform.position))
					{
						ChestAccess.Ready(item);
					}
				}
				if (Automate.Value)
				{
					Production.Tick();
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)ex);
			}
		}

		private void LateUpdate()
		{
			ChestUi.PositionSearch();
			StorageSearchDim.Update();
		}

		private void OnDestroy()
		{
			Harmony obj = patches;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			ChestUi.Dispose();
			Production.Dispose();
			Self = null;
		}

		internal static void Say(string text)
		{
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, "Workshop Worker: " + text, 0, (Sprite)null, false);
			}
		}

		internal static void Log(string text)
		{
			Plugin self = Self;
			if (self != null)
			{
				((BaseUnityPlugin)self).Logger.LogWarning((object)text);
			}
		}

		internal static void LogInfo(string text)
		{
			Plugin self = Self;
			if (self != null)
			{
				((BaseUnityPlugin)self).Logger.LogInfo((object)text);
			}
		}
	}
	internal static class GameApi
	{
		internal static readonly BindingFlags Fields = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		internal static T Get<T>(object obj, string name)
		{
			return (T)obj.GetType().GetField(name, Fields).GetValue(obj);
		}

		internal static object Call(object obj, string name, params object[] args)
		{
			return AccessTools.Method(obj.GetType(), name, (Type[])null, (Type[])null).Invoke(obj, args);
		}

		internal static ZNetView View(Component obj)
		{
			Container val = (Container)(object)((obj is Container) ? obj : null);
			if (val == null || !Object.op_Implicit((Object)(object)val.m_rootObjectOverride))
			{
				if (!Object.op_Implicit((Object)(object)obj))
				{
					return null;
				}
				return obj.GetComponent<ZNetView>();
			}
			return val.m_rootObjectOverride;
		}

		internal static bool Owned(Component obj)
		{
			ZNetView val = View(obj);
			if (Object.op_Implicit((Object)(object)val) && val.IsValid())
			{
				return val.IsOwner();
			}
			return false;
		}

		internal static void Changed(Inventory inventory)
		{
			Call(inventory, "Changed", false, false);
		}

		internal static bool Nearby(Component obj, Vector3 center)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)obj))
			{
				Vector3 val = obj.transform.position - center;
				return ((Vector3)(ref val)).sqrMagnitude <= Plugin.Range.Value * Plugin.Range.Value;
			}
			return false;
		}
	}
	internal static class Production
	{
		private const string Key = "WorkshopWorker.links.v2";

		private const string IdentityKey = "WorkshopWorker.endpoint.v1";

		private static string savedJson;

		private static string savedWorld;

		private static readonly Dictionary<LinkEndpoint, Component> resolved = new Dictionary<LinkEndpoint, Component>();

		private static Component selection;

		private static Component completedMachine;

		private static float lingerUntil;

		private static string role;

		private static bool removing;

		private static float expires;

		private static LinkBook book = new LinkBook();

		private static Player savedPlayer;

		private static readonly List<LineRenderer> lines = new List<LineRenderer>();

		private static Material lineMaterial;

		internal static bool LinkHeld
		{
			get
			{
				//IL_000c: 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)
				if (!InventoryGui.IsVisible())
				{
					if (!LinkModifier(Plugin.InputKey.Value))
					{
						return LinkModifier(Plugin.OutputKey.Value);
					}
					return true;
				}
				return false;
			}
		}

		private static LinkEndpoint Snapshot(Component c)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			ZNetView obj = GameApi.View(c);
			ZDO val = ((obj != null) ? obj.GetZDO() : null);
			if (val == null || !Object.op_Implicit((Object)(object)ZNet.instance))
			{
				return null;
			}
			Vector3 position = val.GetPosition();
			return new LinkEndpoint
			{
				world = ZNet.instance.GetWorldUID().ToString(),
				guid = val.GetString("WorkshopWorker.endpoint.v1", ""),
				prefab = val.GetPrefab(),
				x = position.x,
				y = position.y,
				z = position.z
			};
		}

		private static LinkEndpoint Capture(Component c)
		{
			LinkEndpoint linkEndpoint = Snapshot(c);
			if (linkEndpoint == null)
			{
				return null;
			}
			Promote(linkEndpoint, c);
			resolved[linkEndpoint] = c;
			return linkEndpoint;
		}

		private static bool Promote(LinkEndpoint endpoint, Component c)
		{
			ZNetView obj = GameApi.View(c);
			ZDO val = ((obj != null) ? obj.GetZDO() : null);
			if (val == null || !string.IsNullOrEmpty(endpoint.guid))
			{
				return false;
			}
			string text = val.GetString("WorkshopWorker.endpoint.v1", "");
			if (string.IsNullOrEmpty(text) && GameApi.Owned(c))
			{
				text = Guid.NewGuid().ToString("N");
				val.Set("WorkshopWorker.endpoint.v1", text);
			}
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			endpoint.guid = text;
			return true;
		}

		private static bool Matches(LinkEndpoint endpoint, Component c)
		{
			if (endpoint == null || !Object.op_Implicit((Object)(object)c) || endpoint.world != savedWorld)
			{
				return false;
			}
			if (resolved.TryGetValue(endpoint, out var value) && Object.op_Implicit((Object)(object)value))
			{
				if ((Object)(object)value != (Object)(object)c)
				{
					return false;
				}
			}
			else
			{
				if (!endpoint.Matches(Snapshot(c)))
				{
					return false;
				}
				resolved[endpoint] = c;
			}
			if (Promote(endpoint, c))
			{
				Save();
			}
			return true;
		}

		private static void Save()
		{
			if (Object.op_Implicit((Object)(object)savedPlayer))
			{
				savedJson = ProductionLinkCodec.Serialize(book);
				savedPlayer.m_customData["WorkshopWorker.links.v2"] = savedJson;
			}
		}

		private static void Load()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || !Object.op_Implicit((Object)(object)ZNet.instance))
			{
				return;
			}
			localPlayer.m_customData.TryGetValue("WorkshopWorker.links.v2", out var value);
			string text = ZNet.instance.GetWorldUID().ToString();
			if (!((Object)(object)savedPlayer == (Object)(object)localPlayer) || !(savedWorld == text) || !(savedJson == value))
			{
				savedPlayer = localPlayer;
				savedWorld = text;
				savedJson = value;
				selection = null;
				completedMachine = null;
				resolved.Clear();
				ClearVisuals();
				try
				{
					book = (string.IsNullOrEmpty(value) ? new LinkBook() : ProductionLinkCodec.Deserialize(value));
				}
				catch (Exception ex)
				{
					Plugin.Log("Could not read production links: " + ex.Message);
					book = new LinkBook();
				}
				if (book == null)
				{
					book = new LinkBook();
				}
				LinkBook linkBook = book;
				if (linkBook.links == null)
				{
					linkBook.links = new List<MachineLink>();
				}
				book.links.RemoveAll((MachineLink l) => l == null || l.machine == null || l.chest == null);
				Plugin.LogInfo("Loaded " + book.links.Count + " production links from character save.");
			}
		}

		private static MachineLink[] MachineLinks(Component machine)
		{
			Load();
			return book.links.Where((MachineLink l) => Matches(l.machine, machine)).ToArray();
		}

		internal static string Summary(Component machine)
		{
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return "";
			}
			Smelter val = (Smelter)(object)((machine is Smelter) ? machine : null);
			int num;
			if (val == null || !Object.op_Implicit((Object)(object)val.m_fuelItem) || val.m_maxFuel <= 0)
			{
				CookingStation val2 = (CookingStation)(object)((machine is CookingStation) ? machine : null);
				if (val2 == null || !val2.m_useFuel || !Object.op_Implicit((Object)(object)val2.m_fuelItem))
				{
					Fireplace val3 = (Fireplace)(object)((machine is Fireplace) ? machine : null);
					num = ((val3 != null && val3.m_canRefill && !val3.m_infiniteFuel && Object.op_Implicit((Object)(object)val3.m_fuelItem)) ? 1 : 0);
					goto IL_007c;
				}
			}
			num = 1;
			goto IL_007c;
			IL_007c:
			bool fuel = (byte)num != 0;
			return ProductionLinkSummary.Format(MachineLinks(machine), !(machine is Fireplace), fuel, !(machine is Fireplace));
		}

		internal static Component HoverMachine()
		{
			Player localPlayer = Player.m_localPlayer;
			GameObject val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetHoverObject() : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			return (Component)(val.GetComponentInParent<Smelter>() ?? val.GetComponentInParent<CookingStation>() ?? ((object)val.GetComponentInParent<Fermenter>()) ?? ((object)val.GetComponentInParent<Fireplace>()));
		}

		private static bool Down(KeyboardShortcut key)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(((KeyboardShortcut)(ref key)).MainKey))
			{
				return ((KeyboardShortcut)(ref key)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey);
			}
			return false;
		}

		private static bool LinkModifier(KeyboardShortcut key)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (!((KeyboardShortcut)(ref key)).Modifiers.Any())
			{
				if (Object.op_Implicit((Object)(object)HoverMachine()) || Object.op_Implicit((Object)(object)selection))
				{
					return Input.GetKey(((KeyboardShortcut)(ref key)).MainKey);
				}
				return false;
			}
			return ((KeyboardShortcut)(ref key)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey);
		}

		internal static void Gestures()
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: 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)
			if (ChestUi.BlockGameplayInput)
			{
				return;
			}
			Load();
			if (Object.op_Implicit((Object)(object)selection) && Time.unscaledTime > expires)
			{
				selection = null;
				Plugin.Say("Link selection expired.");
			}
			if (Object.op_Implicit((Object)(object)completedMachine) && Time.unscaledTime >= lingerUntil)
			{
				completedMachine = null;
			}
			bool flag;
			Component val;
			int num2;
			if (!InventoryGui.IsVisible() && !Chat.instance.HasFocus())
			{
				flag = Down(Plugin.OutputKey.Value);
				bool flag2 = Down(Plugin.InputKey.Value);
				if (flag || flag2)
				{
					GameObject hoverObject = ((Humanoid)Player.m_localPlayer).GetHoverObject();
					Container chest = (Object.op_Implicit((Object)(object)hoverObject) ? hoverObject.GetComponentInParent<Container>() : null);
					if (Object.op_Implicit((Object)(object)selection) && Object.op_Implicit((Object)(object)chest))
					{
						if (role == "Output" != flag)
						{
							Plugin.Say("Use the gesture matching the selected role.");
							return;
						}
						if (!ChestAccess.Allowed(chest) || !GameApi.Nearby((Component)(object)chest, selection.transform.position))
						{
							Plugin.Say("Chest is out of range or access is denied.");
						}
						else
						{
							LinkEndpoint linkEndpoint = Capture(selection);
							LinkEndpoint linkEndpoint2 = Capture((Component)(object)chest);
							if (linkEndpoint == null || linkEndpoint2 == null)
							{
								return;
							}
							book.links.RemoveAll((MachineLink l) => Matches(l.machine, selection) && Matches(l.chest, (Component)(object)chest) && l.role == role);
							int num;
							if (!removing)
							{
								num = (Input.GetKey(Plugin.ClearKey.Value) ? 1 : 0);
								if (num == 0)
								{
									book.links.Add(new MachineLink
									{
										machine = linkEndpoint,
										chest = linkEndpoint2,
										role = role
									});
								}
							}
							else
							{
								num = 1;
							}
							Save();
							completedMachine = selection;
							lingerUntil = Time.unscaledTime + 5f;
							selection = null;
							Plugin.Say(((num != 0) ? "Removed " : "Added ") + role + " chest.");
						}
					}
					else
					{
						val = HoverMachine();
						if (Object.op_Implicit((Object)(object)val) && PrivateArea.CheckAccess(val.transform.position, 0f, false, false))
						{
							Switch componentInParent = hoverObject.GetComponentInParent<Switch>();
							if (!(val is Fireplace))
							{
								Smelter val2 = (Smelter)(object)((val is Smelter) ? val : null);
								if (val2 == null || !Object.op_Implicit((Object)(object)componentInParent) || !((Object)(object)componentInParent == (Object)(object)val2.m_addWoodSwitch))
								{
									CookingStation val3 = (CookingStation)(object)((val is CookingStation) ? val : null);
									num2 = ((val3 != null && Object.op_Implicit((Object)(object)componentInParent) && (Object)(object)componentInParent == (Object)(object)val3.m_addFuelSwitch) ? 1 : 0);
									goto IL_029a;
								}
							}
							num2 = 1;
							goto IL_029a;
						}
					}
				}
			}
			goto IL_0313;
			IL_0313:
			Component val4 = HoverMachine();
			Draw((!Plugin.ShowVfx.Value) ? null : (Object.op_Implicit((Object)(object)val4) ? val4 : (Object.op_Implicit((Object)(object)selection) ? selection : completedMachine)));
			return;
			IL_029a:
			bool flag3 = (byte)num2 != 0;
			selection = val;
			completedMachine = null;
			role = (flag ? "Output" : (flag3 ? "Fuel" : "Input"));
			removing = Input.GetKey(Plugin.ClearKey.Value);
			expires = Time.unscaledTime + 30f;
			Plugin.Say((removing ? "Remove " : "Link ") + role + ": use the same gesture on a chest within 30 seconds.");
			goto IL_0313;
		}

		internal static IEnumerable<Container> Linked(Component machine, string kind)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			MachineLink[] links = (from l in MachineLinks(machine)
				where l.role == kind
				select l).ToArray();
			return from c in ChestAccess.Near(machine.transform.position)
				where links.Any((MachineLink l) => Matches(l.chest, (Component)(object)c))
				select c;
		}

		internal static IEnumerable<ItemDrop> Ingredients(Component m, bool fuel)
		{
			Smelter val = (Smelter)(object)((m is Smelter) ? m : null);
			if (val != null)
			{
				if (!fuel)
				{
					return from i in val.m_conversion
						select i.m_from into i
						where Object.op_Implicit((Object)(object)i)
						select i;
				}
				return ((IEnumerable<ItemDrop>)(object)new ItemDrop[1] { val.m_fuelItem }).Where((ItemDrop i) => Object.op_Implicit((Object)(object)i));
			}
			CookingStation val2 = (CookingStation)(object)((m is CookingStation) ? m : null);
			if (val2 != null)
			{
				if (!fuel)
				{
					return from i in val2.m_conversion
						select i.m_from into i
						where Object.op_Implicit((Object)(object)i)
						select i;
				}
				return ((IEnumerable<ItemDrop>)(object)new ItemDrop[1] { val2.m_fuelItem }).Where((ItemDrop i) => Object.op_Implicit((Object)(object)i));
			}
			Fermenter val3 = (Fermenter)(object)((m is Fermenter) ? m : null);
			if (val3 != null)
			{
				return from i in val3.m_conversion
					select i.m_from into i
					where Object.op_Implicit((Object)(object)i)
					select i;
			}
			Fireplace val4 = (Fireplace)(object)((m is Fireplace) ? m : null);
			if (val4 != null)
			{
				return ((IEnumerable<ItemDrop>)(object)new ItemDrop[1] { val4.m_fuelItem }).Where((ItemDrop i) => Object.op_Implicit((Object)(object)i));
			}
			return Array.Empty<ItemDrop>();
		}

		private static void Feed(Component machine, string kind, Func<bool> room, string rpc, Func<ItemData, object[]> args)
		{
			if (!room())
			{
				return;
			}
			HashSet<string> allowed = new HashSet<string>(from i in Ingredients(machine, kind == "Fuel")
				select ((Object)i).name);
			foreach (Container item in Linked(machine, kind))
			{
				if (ChestAccess.Ready(item))
				{
					ItemData val = ((IEnumerable<ItemData>)item.GetInventory().GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => allowed.Contains(Storage.ItemId(i)) && i.m_worldLevel >= Game.m_worldLevel));
					if (val != null && GameApi.Owned(machine) && room())
					{
						GameApi.Call(machine, rpc, args(val));
						item.GetInventory().RemoveItem(val, 1);
						break;
					}
				}
			}
		}

		internal static void Tick()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0418: Unknown result type (might be due to invalid IL or missing references)
			Load();
			Component[] machines = Plugin.Machines;
			Smelter s = default(Smelter);
			CookingStation c = default(CookingStation);
			Fermenter f = default(Fermenter);
			Fireplace firePlace = default(Fireplace);
			foreach (Component m in machines)
			{
				if (!Object.op_Implicit((Object)(object)m) || !GameApi.Owned(m) || !GameApi.Nearby(m, ((Component)Player.m_localPlayer).transform.position) || !PrivateArea.CheckAccess(m.transform.position, 0f, false, false))
				{
					continue;
				}
				foreach (Container item in Linked(m, "Output"))
				{
					ChestAccess.Ready(item);
				}
				ref Smelter reference = ref s;
				Component obj = m;
				reference = (Smelter)(object)((obj is Smelter) ? obj : null);
				if (s != null)
				{
					Feed((Component)(object)s, "Input", () => (int)GameApi.Call(s, "GetQueueSize") < s.m_maxOre, "RPC_AddOre", (ItemData val2) => new object[3]
					{
						ZNet.GetUID(),
						Storage.ItemId(val2),
						val2.m_cheated
					});
					if (Object.op_Implicit((Object)(object)s.m_fuelItem) && s.m_maxFuel > 0)
					{
						Feed((Component)(object)s, "Fuel", () => (float)GameApi.Call(s, "GetFuel") <= (float)s.m_maxFuel - 1f, "RPC_AddFuel", (ItemData val2) => new object[1] { ZNet.GetUID() });
					}
					int num = (int)GameApi.Call(s, "GetProcessedQueueSize");
					string ore = GameApi.View((Component)(object)s).GetZDO().GetString(ZDOVars.s_spawnOre, "");
					ItemDrop prefab = ((IEnumerable<ItemConversion>)s.m_conversion).FirstOrDefault((Func<ItemConversion, bool>)((ItemConversion x) => !Object.op_Implicit((Object)(object)x.m_from) || ((Object)x.m_from).name == ore))?.m_to;
					if (num > 0 && OutputRoom((Component)(object)s, prefab, num))
					{
						GameApi.Call(s, "RPC_EmptyProcessed", ZNet.GetUID());
					}
					continue;
				}
				ref CookingStation reference2 = ref c;
				Component obj2 = m;
				reference2 = (CookingStation)(object)((obj2 is CookingStation) ? obj2 : null);
				if (c != null)
				{
					if (!c.m_requireFire || (bool)GameApi.Call(c, "IsFireLit"))
					{
						Feed((Component)(object)c, "Input", () => (int)GameApi.Call(c, "GetFreeSlot") >= 0, "RPC_AddItem", (ItemData val2) => new object[3]
						{
							ZNet.GetUID(),
							Storage.ItemId(val2),
							val2.m_cheated
						});
					}
					if (c.m_useFuel && Object.op_Implicit((Object)(object)c.m_fuelItem))
					{
						Feed((Component)(object)c, "Fuel", () => (float)GameApi.Call(c, "GetFuel") <= (float)c.m_maxFuel - 1f, "RPC_AddFuel", (ItemData val2) => new object[1] { ZNet.GetUID() });
					}
					for (int num2 = 0; num2 < c.m_slots.Length; num2++)
					{
						object[] array = new object[5] { num2, null, 0f, null, false };
						GameApi.Call(c, "GetSlot", array);
						string text = (string)array[1];
						if (!string.IsNullOrEmpty(text) && (bool)GameApi.Call(c, "IsItemDone", text))
						{
							GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text);
							ItemDrop prefab2 = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null);
							if (OutputRoom((Component)(object)c, prefab2, 1, (bool)array[4]))
							{
								GameApi.Call(c, "RPC_RemoveDoneItem", ZNet.GetUID(), ((Component)Player.m_localPlayer).transform.position, 1);
							}
							break;
						}
					}
					continue;
				}
				ref Fermenter reference3 = ref f;
				Component obj3 = m;
				reference3 = (Fermenter)(object)((obj3 is Fermenter) ? obj3 : null);
				if (f != null)
				{
					GameApi.Call(f, "UpdateCover", 0f, true);
					if (GameApi.Get<bool>(f, "m_hasRoof") && !GameApi.Get<bool>(f, "m_exposed"))
					{
						Feed((Component)(object)f, "Input", () => (int)GameApi.Call(f, "GetContent") == 0, "RPC_AddItem", (ItemData val2) => new object[3]
						{
							ZNet.GetUID(),
							StringExtensionMethods.GetStableHashCode(Storage.ItemId(val2)),
							val2.m_cheated
						});
					}
					if (GameApi.Call(f, "GetStatus").ToString() == "Ready")
					{
						int content = (int)GameApi.Call(f, "GetContent");
						ItemConversion val = ((IEnumerable<ItemConversion>)f.m_conversion).FirstOrDefault((Func<ItemConversion, bool>)((ItemConversion x) => StringExtensionMethods.GetStableHashCode(((Object)x.m_from).name) == content));
						if (val != null && OutputRoom((Component)(object)f, val.m_to, val.m_producedItems))
						{
							GameApi.Call(f, "RPC_Tap", ZNet.GetUID());
						}
					}
					continue;
				}
				ref Fireplace reference4 = ref firePlace;
				Component obj4 = m;
				reference4 = (Fireplace)(object)((obj4 is Fireplace) ? obj4 : null);
				if (firePlace != null && Object.op_Implicit((Object)(object)firePlace.m_fuelItem) && !firePlace.m_infiniteFuel && firePlace.m_canRefill)
				{
					Feed(m, "Fuel", () => GameApi.View(m).GetZDO().GetFloat(ZDOVars.s_fuel, 0f) <= firePlace.m_maxFuel - 1f, "RPC_AddFuel", (ItemData val2) => new object[1] { ZNet.GetUID() });
				}
			}
		}

		private static bool OutputRoom(Component machine, ItemDrop prefab, int amount, bool cheated = false)
		{
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				return false;
			}
			ItemData item = prefab.m_itemData.Clone();
			item.m_dropPrefab = ((Component)prefab).gameObject;
			item.m_worldLevel = (byte)Game.m_worldLevel;
			ZDO zDO = GameApi.View(machine).GetZDO();
			item.m_cheated = (cheated || zDO.GetBool(ZDOVars.s_cheatedQueued, false) || zDO.GetBool(ZDOVars.s_cheated, false)) && !PlayerProfile.s_bypassCheatChecks;
			CookingStation val = (CookingStation)(object)((machine is CookingStation) ? machine : null);
			if (val != null)
			{
				if (val.m_spawnFullDurability)
				{
					item.m_durability = item.m_shared.m_maxDurability;
				}
				if (val.m_recordCrafter)
				{
					item.m_crafterID = Player.m_localPlayer.GetPlayerID();
					item.m_crafterName = Player.m_localPlayer.GetPlayerName();
				}
			}
			return Linked(machine, "Output").Where(ChestAccess.Ready).Sum((Container c) => Transfer.Capacity(c.GetInventory(), item)) >= amount;
		}

		internal static void Deposit(Component machine, ItemDrop drop)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)machine) || !Object.op_Implicit((Object)(object)drop) || !GameApi.Owned(machine) || !GameApi.Owned((Component)(object)drop) || !Plugin.Automate.Value || !PrivateArea.CheckAccess(machine.transform.position, 0f, false, false))
			{
				return;
			}
			Container[] array = Linked(machine, "Output").Where(ChestAccess.Ready).ToArray();
			if (array.Sum((Container c) => Transfer.Capacity(c.GetInventory(), drop.m_itemData)) < drop.m_itemData.m_stack)
			{
				return;
			}
			Inventory val = new Inventory("Workshop Worker output", (Sprite)null, 1, 1);
			val.GetAllItems().Add(drop.m_itemData);
			try
			{
				Container[] array2 = array;
				foreach (Container val2 in array2)
				{
					if (!ChestAccess.Ready(val2))
					{
						break;
					}
					Transfer.Move(val, val2.GetInventory(), drop.m_itemData, drop.m_itemData.m_stack);
					if (drop.m_itemData.m_stack == 0)
					{
						break;
					}
				}
			}
			finally
			{
				if (drop.m_itemData.m_stack == 0)
				{
					ZNetScene.instance.Destroy(((Component)drop).gameObject);
				}
				else
				{
					GameApi.Call(drop, "Save");
				}
			}
		}

		private static void Draw(Component m)
		{
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: 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_005e: 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)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: 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)
			int num = 0;
			if (Object.op_Implicit((Object)(object)m))
			{
				string[] array = new string[3] { "Input", "Fuel", "Output" };
				foreach (string text in array)
				{
					foreach (Container item in Linked(m, text))
					{
						Line(num++, m.transform.position + Vector3.up, ((Component)item).transform.position + Vector3.up * 0.5f, (Color)((text == "Input") ? Color.cyan : ((text == "Fuel") ? new Color(1f, 0.5f, 0f) : Color.green)));
					}
				}
				if ((Object)(object)selection == (Object)(object)m)
				{
					Vector3 val = m.transform.position + Vector3.up;
					Smelter val2 = (Smelter)(object)((m is Smelter) ? m : null);
					if (val2 != null)
					{
						Switch obj = ((role == "Fuel") ? val2.m_addWoodSwitch : val2.m_addOreSwitch);
						val = ((obj != null) ? ((Component)obj).transform.position : val);
					}
					CookingStation val3 = (CookingStation)(object)((m is CookingStation) ? m : null);
					if (val3 != null)
					{
						Switch obj2 = ((role == "Fuel") ? val3.m_addFuelSwitch : val3.m_addFoodSwitch);
						val = ((obj2 != null) ? ((Component)obj2).transform.position : val);
					}
					for (int j = 0; j < 16; j++)
					{
						Line(num++, val + new Vector3(Mathf.Cos((float)j * MathF.PI / 8f), 0.1f, Mathf.Sin((float)j * MathF.PI / 8f)) * 0.22f, val + new Vector3(Mathf.Cos((float)(j + 1) * MathF.PI / 8f), 0.1f, Mathf.Sin((float)(j + 1) * MathF.PI / 8f)) * 0.22f, Color.yellow);
					}
				}
			}
			for (int k = num; k < lines.Count; k++)
			{
				if (Object.op_Implicit((Object)(object)lines[k]))
				{
					((Component)lines[k]).gameObject.SetActive(false);
				}
			}
		}

		private static void Line(int index, Vector3 start, Vector3 end, Color tint)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)lineMaterial))
			{
				lineMaterial = new Material(Shader.Find("Sprites/Default"));
			}
			LineRenderer obj = LiveObjectPool.Get(lines, index, (LineRenderer line) => Object.op_Implicit((Object)(object)line), delegate
			{
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				LineRenderer obj2 = new GameObject("WorkshopWorkerLink").AddComponent<LineRenderer>();
				obj2.positionCount = 2;
				obj2.useWorldSpace = true;
				float startWidth = (obj2.endWidth = 0.035f);
				obj2.startWidth = startWidth;
				return obj2;
			});
			((Renderer)obj).sharedMaterial = lineMaterial;
			((Component)obj).gameObject.SetActive(true);
			Color startColor = (obj.endColor = tint);
			obj.startColor = startColor;
			obj.SetPosition(0, start);
			obj.SetPosition(1, end);
		}

		private static void ClearVisuals()
		{
			foreach (LineRenderer line in lines)
			{
				if (Object.op_Implicit((Object)(object)line))
				{
					Object.Destroy((Object)(object)((Component)line).gameObject);
				}
			}
			lines.Clear();
			if (Object.op_Implicit((Object)(object)lineMaterial))
			{
				Object.Destroy((Object)(object)lineMaterial);
			}
			lineMaterial = null;
		}

		internal static void Dispose()
		{
			ClearVisuals();
			resolved.Clear();
			selection = null;
			completedMachine = null;
			savedPlayer = null;
			savedJson = null;
			savedWorld = null;
			book = new LinkBook();
		}
	}
	internal sealed class OutputCapture
	{
		internal static OutputCapture Current;

		internal Component machine;

		internal OutputCapture previous;

		internal readonly List<ItemDrop> drops = new List<ItemDrop>();
	}
	[HarmonyPatch]
	internal static class ProductionHoverLinks
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(Switch), "GetHoverText", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(CookingStation), "GetHoverText", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Fermenter), "GetHoverText", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Fireplace), "GetHoverText", (Type[])null, (Type[])null);
		}

		private static void Postfix(Component __instance, ref string __result)
		{
			Component val = (Component)((__instance is Switch) ? (__instance.GetComponentInParent<Smelter>() ?? __instance.GetComponentInParent<CookingStation>() ?? ((object)__instance.GetComponentInParent<Fermenter>()) ?? ((object)__instance.GetComponentInParent<Fireplace>())) : __instance);
			if (Object.op_Implicit((Object)(object)val))
			{
				string text = Production.Summary(val);
				if (text.Length > 0 && !(__result ?? "").Contains(text))
				{
					__result = __result + "\n" + text;
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class NativeOutput
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(Smelter), "Spawn", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(CookingStation), "SpawnItem", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Fermenter), "DelayedTap", (Type[])null, (Type[])null);
		}

		private static void Prefix(Component __instance, out OutputCapture __state)
		{
			__state = new OutputCapture
			{
				machine = __instance,
				previous = OutputCapture.Current
			};
			OutputCapture.Current = __state;
		}

		private static Exception Finalizer(Exception __exception, OutputCapture __state)
		{
			OutputCapture.Current = __state.previous;
			if (__exception == null)
			{
				foreach (ItemDrop drop in __state.drops)
				{
					try
					{
						Production.Deposit(__state.machine, drop);
					}
					catch (Exception ex)
					{
						Plugin.Log("Output routing failed: " + ex);
					}
				}
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(ItemDrop), "OnCreateNew", new Type[]
	{
		typeof(ItemDrop),
		typeof(bool)
	})]
	internal static class CaptureCreatedDrop
	{
		private static void Postfix(ItemDrop __0)
		{
			if (Object.op_Implicit((Object)(object)__0))
			{
				OutputCapture.Current?.drops.Add(__0);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "PlayerAttackInput")]
	internal static class LinkAttackGuard
	{
		private static void Prefix(Player __instance, ref bool ___m_attack, ref bool ___m_attackHold, ref bool ___m_secondaryAttack, ref bool ___m_secondaryAttackHold, ref float ___m_queuedAttackTimer, ref float ___m_queuedSecondAttackTimer)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Production.LinkHeld)
			{
				___m_attack = (___m_attackHold = (___m_secondaryAttack = (___m_secondaryAttackHold = false)));
				___m_queuedAttackTimer = (___m_queuedSecondAttackTimer = 0f);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "SetControls")]
	internal static class LinkControlsGuard
	{
		[HarmonyPriority(0)]
		private static void Prefix(Player __instance, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold, ref bool block, ref bool blockHold)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Production.LinkHeld)
			{
				attack = (attackHold = (secondaryAttack = (secondaryAttackHold = (block = (blockHold = false)))));
			}
		}
	}
	[DataContract]
	internal sealed class LinkEndpoint
	{
		[DataMember(IsRequired = true)]
		public string world;

		[DataMember]
		public string guid;

		[DataMember(IsRequired = true)]
		public int prefab;

		[DataMember(IsRequired = true)]
		public float x;

		[DataMember(IsRequired = true)]
		public float y;

		[DataMember(IsRequired = true)]
		public float z;

		internal string Identity
		{
			get
			{
				if (string.IsNullOrEmpty(guid))
				{
					return FormattableString.Invariant($"{prefab}:{x:R}:{y:R}:{z:R}");
				}
				return guid;
			}
		}

		internal bool Matches(LinkEndpoint other)
		{
			if (other == null || world != other.world || prefab != other.prefab)
			{
				return false;
			}
			if (!string.IsNullOrEmpty(guid))
			{
				return guid == other.guid;
			}
			if (Math.Abs(x - other.x) < 0.01f && Math.Abs(y - other.y) < 0.01f)
			{
				return Math.Abs(z - other.z) < 0.01f;
			}
			return false;
		}
	}
	[DataContract]
	internal sealed class MachineLink
	{
		[DataMember(IsRequired = true)]
		public LinkEndpoint machine;

		[DataMember(IsRequired = true)]
		public LinkEndpoint chest;

		[DataMember(IsRequired = true)]
		public string role;
	}
	[DataContract]
	internal sealed class LinkBook
	{
		[DataMember(IsRequired = true)]
		public List<MachineLink> links = new List<MachineLink>();
	}
	internal static class ProductionLinkCodec
	{
		internal static string Serialize(LinkBook book)
		{
			using MemoryStream memoryStream = new MemoryStream();
			new DataContractJsonSerializer(typeof(LinkBook)).WriteObject(memoryStream, book);
			string text = Encoding.UTF8.GetString(memoryStream.ToArray());
			if (Deserialize(text).links.Count != book.links.Count)
			{
				throw new SerializationException("Production link serialization lost records.");
			}
			return text;
		}

		internal static LinkBook Deserialize(string json)
		{
			using MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
			LinkBook linkBook = (LinkBook)new DataContractJsonSerializer(typeof(LinkBook)).ReadObject(stream);
			if (linkBook?.links == null || linkBook.links.Any((MachineLink l) => l?.machine == null || l.chest == null || string.IsNullOrEmpty(l.machine.world) || string.IsNullOrEmpty(l.chest.world) || (!(l.role == "Input") && !(l.role == "Fuel") && !(l.role == "Output"))))
			{
				throw new SerializationException("Invalid production link records.");
			}
			return linkBook;
		}
	}
	internal static class ProductionLinkSummary
	{
		internal static string Format(IEnumerable<MachineLink> links, bool input, bool fuel, bool output)
		{
			List<string> list = new List<string>();
			string[] array = new string[3] { "Input", "Fuel", "Output" };
			foreach (string role in array)
			{
				if ((role == "Input") ? input : ((role == "Fuel") ? fuel : output))
				{
					int num = (from l in links
						where l.role == role && l.chest != null
						select l.chest.Identity).Distinct().Count();
					if (num > 0)
					{
						list.Add(role + ": " + num);
					}
				}
			}
			return string.Join("   ", list);
		}
	}
	internal static class Repairs
	{
		private static readonly Queue<WearNTear> pending = new Queue<WearNTear>();

		private static Player actor;

		private static Vector3 center;

		private static int requested;

		internal static void Begin()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			actor = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)actor) || ((Character)actor).IsDead())
			{
				return;
			}
			center = ((Component)actor).transform.position;
			requested = 0;
			pending.Clear();
			foreach (WearNTear item in Object.FindObjectsByType<WearNTear>((FindObjectsSortMode)0).Where(delegate(WearNTear w)
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_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 (Object.op_Implicit((Object)(object)((Component)w).GetComponent<Piece>()))
				{
					Vector3 val = ((Component)w).transform.position - center;
					return ((Vector3)(ref val)).sqrMagnitude <= 10000f;
				}
				return false;
			}))
			{
				pending.Enqueue(item);
			}
			if (pending.Count == 0)
			{
				Plugin.Say("No loaded structures within 100 m.");
			}
		}

		internal static void