Decompiled source of EasyCraftAndBuildFromOwnChests v1.3.0

BepInEx/plugins/EasyCraftAndBuildFromOwnChests/EasyCraftAndBuildFromOwnChests.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.4.10.0")]
namespace EasyCraftAndBuildFromOwnChests;

[BepInPlugin("com.modmaking.easycraftfromownchests", "Easy Craft & Build from own chests", "0.4.10")]
public sealed class Plugin : BaseUnityPlugin
{
	private sealed class BuildReceipt
	{
		internal Player Player;

		internal Requirement[] Resources;

		internal int Frame;

		internal bool Consumed;
	}

	private sealed class Request
	{
		internal long Owner;

		internal float Sent;

		internal bool Granted;

		internal bool TimedOut;

		internal bool Completed;
	}

	internal static int PlacementScope;

	internal static int BuildDisplayDepth;

	private static bool buildingWorldStarted;

	private static bool buildingFailure;

	private static CraftTransaction building;

	private static BuildReceipt receipt;

	public const string Id = "com.modmaking.easycraftfromownchests";

	internal static Plugin Instance;

	internal static ConfigEntry<bool> Enabled;

	private static ConfigEntry<string> ExcludedPrefabs;

	private static readonly HashSet<string> exclusions = new HashSet<string>(StringComparer.Ordinal);

	internal static readonly ResourceCache Cache = new ResourceCache();

	internal static CraftTransaction Current;

	internal static int SuppressCount;

	internal static int opening;

	internal static int panelDepth;

	private static bool installed;

	private static bool faulted;

	private static bool wasRunning;

	private static Harmony harmony;

	private static float nextPanel;

	internal static bool DisplayExcluded;

	internal static readonly MethodInfo Load = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null);

	internal static readonly MethodInfo Save = AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null);

	internal static readonly MethodInfo Access = AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null);

	internal static readonly MethodInfo Changed = AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null);

	internal static readonly MethodInfo SetupEquipment = AccessTools.Method(typeof(Humanoid), "SetupEquipment", (Type[])null, (Type[])null);

	internal static readonly FieldInfo InUse = AccessTools.Field(typeof(Container), "m_inUse");

	private static readonly FieldInfo RecipeField = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe");

	private static readonly FieldInfo UpgradeField = AccessTools.Field(typeof(InventoryGui), "m_craftUpgradeItem");

	private static readonly FieldInfo MultiField = AccessTools.Field(typeof(InventoryGui), "m_multiCrafting");

	private static readonly FieldInfo AmountField = AccessTools.Field(typeof(InventoryGui), "m_multiCraftAmount");

	private static readonly FieldInfo SelectedField = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe");

	private static readonly MethodInfo SelectedGetter = ((SelectedField == null) ? null : AccessTools.PropertyGetter(SelectedField.FieldType, "Recipe"));

	internal static readonly MethodInfo RefreshPanel = AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", (Type[])null, (Type[])null);

	private static readonly Dictionary<Container, Request> requests = new Dictionary<Container, Request>();

	private static Container manual;

	private static long manualPlayer;

	private static long manualOwner;

	private static float manualUntil;

	private static float requestWindow;

	private static int sentInWindow;

	internal static bool Running
	{
		get
		{
			if (installed && !faulted && Enabled != null)
			{
				return Enabled.Value;
			}
			return false;
		}
	}

	internal static bool Active
	{
		get
		{
			Player localPlayer = Player.m_localPlayer;
			if (Running && Object.op_Implicit((Object)(object)localPlayer))
			{
				if (!((Character)localPlayer).InPlaceMode())
				{
					if (Object.op_Implicit((Object)(object)localPlayer.GetCurrentCraftingStation()))
					{
						if (opening <= 0 && panelDepth <= 0)
						{
							return InventoryGui.IsVisible();
						}
						return true;
					}
					return false;
				}
				return true;
			}
			return false;
		}
	}

	internal static void BeginBuildDisplay()
	{
		BuildDisplayDepth++;
		if (Running && Current == null)
		{
			Cache.Flush();
		}
	}

	internal static void EndBuildDisplay()
	{
		BuildDisplayDepth = Math.Max(0, BuildDisplayDepth - 1);
	}

	internal static void PrepareHammer()
	{
		if (Running && Object.op_Implicit((Object)(object)Player.m_localPlayer) && Current == null)
		{
			SharingService.RequestUse();
			Cache.Poll(all: true);
		}
	}

	public static int BuildCount(Inventory inventory, string name, int quality, bool world)
	{
		int num = inventory.CountItems(name, quality, world);
		if (!Running || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || inventory != ((Humanoid)Player.m_localPlayer).GetInventory())
		{
			return num;
		}
		return checked(num + Cache.Count(name, quality, world));
	}

	public static bool BuildHave(Inventory inventory, string name, bool world)
	{
		return BuildCount(inventory, name, -1, world) > 0;
	}

	public static void PlaceWithResources(Player player, Piece piece, Vector3 pos, Quaternion rot, bool attack, bool cheated)
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01af: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
		if (PlacementScope == 0 || !Running || (Object)(object)player != (Object)(object)Player.m_localPlayer || player.PlacementCostDisabled || ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey()))
		{
			player.PlacePiece(piece, pos, rot, attack, cheated);
			return;
		}
		if (Current != null)
		{
			throw new InvalidOperationException("Nested resource transaction during building");
		}
		CraftTransaction craftTransaction = null;
		try
		{
			Cache.Poll(all: true);
			craftTransaction = new CraftTransaction(player, null);
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val in resources)
			{
				if (Object.op_Implicit((Object)(object)val.m_resItem) && val.m_upgraderResource == (Object.op_Implicit((Object)(object)currentCraftingStation) && currentCraftingStation.m_upgrader))
				{
					craftTransaction.Plan(new ResourceKey(val.m_resItem.m_itemData.m_shared.m_name, -1, world: true), val.GetAmount(0));
				}
			}
			if (craftTransaction.Sources.Count == 0)
			{
				player.PlacePiece(piece, pos, rot, attack, cheated);
				return;
			}
			Current = craftTransaction;
			craftTransaction.Acquire();
			KeyValuePair<ResourceKey, int>[] array = craftTransaction.Remaining.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				KeyValuePair<ResourceKey, int> keyValuePair = array[i];
				craftTransaction.Remove(((Humanoid)player).GetInventory(), keyValuePair.Key.Name, keyValuePair.Value, keyValuePair.Key.Quality, keyValuePair.Key.World);
			}
			craftTransaction.PrepareCommit();
			building = craftTransaction;
			buildingWorldStarted = false;
			bool flag = craftTransaction.Debits.Any((CraftTransaction.Debit d) => d.Item.m_cheated);
			player.PlacePiece(piece, pos, rot, attack, cheated || (flag && !PlayerProfile.s_bypassCheatChecks));
			receipt = new BuildReceipt
			{
				Player = player,
				Resources = piece.m_resources,
				Frame = Time.frameCount
			};
			craftTransaction.Commit();
		}
		catch (Exception error)
		{
			buildingFailure = true;
			if (craftTransaction != null && buildingWorldStarted)
			{
				craftTransaction.Committed = true;
				craftTransaction.Publish();
				Fault("Placement failed after world creation began. Materials remain paid; inspect the placed piece and log before continuing.");
			}
			Finish(error, ownsScope: true);
			throw;
		}
		finally
		{
			building = null;
			buildingWorldStarted = false;
			Finish(null, ownsScope: true);
		}
	}

	public static GameObject InstantiateBuilding(GameObject prefab, Vector3 pos, Quaternion rot)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		if (building != null)
		{
			buildingWorldStarted = true;
		}
		return Object.Instantiate<GameObject>(prefab, pos, rot);
	}

	public static void ConsumeBuilding(Player player, Requirement[] resources, int quality, int itemQuality, int multiplier)
	{
		if (receipt != null)
		{
			BuildReceipt buildReceipt = receipt;
			if (buildReceipt.Consumed || !((Object)(object)buildReceipt.Player == (Object)(object)player) || buildReceipt.Resources != resources || buildReceipt.Frame != Time.frameCount || quality != 0 || itemQuality != -1 || multiplier != 1)
			{
				Fault("Building resource receipt did not match; prevented a second charge.");
				throw new InvalidOperationException("Building payment sequence changed");
			}
			buildReceipt.Consumed = true;
		}
		else
		{
			player.ConsumeResources(resources, quality, itemQuality, multiplier);
		}
	}

	internal static Exception FinishPlacement(Exception error)
	{
		PlacementScope = Math.Max(0, PlacementScope - 1);
		bool flag = receipt != null;
		if (PlacementScope == 0)
		{
			receipt = null;
		}
		bool num = buildingFailure;
		buildingFailure = false;
		if (num)
		{
			try
			{
				TerrainModifier.SetTriggerOnPlaced(false);
			}
			catch (Exception e)
			{
				Warn("Placement trigger cleanup failed", e);
			}
			if (error != null)
			{
				Warn("Building transaction interrupted", error);
			}
			return null;
		}
		if (flag && error != null)
		{
			Warn("Paid placement completed; later building feedback failed (not refunded)", error);
			return null;
		}
		return error;
	}

	private void Awake()
	{
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Expected O, but got Unknown
		Instance = this;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Use loaded chests built by your character within 200 metres for station crafting and building (including Free Build Camera).");
		ExcludedPrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("Compatibility", "InventoryOnlyRecipePrefabs", "Lesly_WaterproofingBrush", "Comma-separated output prefab names whose custom crafting handlers require inventory-only resources.");
		ReadExclusions();
		ExcludedPrefabs.SettingChanged += ExclusionsChanged;
		Enabled.SettingChanged += EnabledChanged;
		harmony = new Harmony("com.modmaking.easycraftfromownchests");
		try
		{
			RequireApi();
			Patches.Install(harmony);
			installed = true;
			Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
			foreach (Container c in array)
			{
				Cache.Add(c);
			}
			wasRunning = Running;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Easy Craft & Build from own chests 0.4.10: 200m player-centred radius; own/shared chest crafting + building; cached counts and guarded transactions. All hooks verified.");
		}
		catch (Exception e)
		{
			faulted = true;
			installed = false;
			try
			{
				harmony.UnpatchSelf();
			}
			catch (Exception e2)
			{
				Warn("Unpatch failed", e2);
			}
			Warn("Incompatible game/mod layout; feature remains disabled until restart", e);
		}
	}

	private static void RequireApi()
	{
		MemberInfo[] array = new MemberInfo[13]
		{
			Load, Save, Access, Changed, SetupEquipment, InUse, RecipeField, UpgradeField, MultiField, AmountField,
			SelectedField, SelectedGetter, RefreshPanel
		};
		for (int i = 0; i < array.Length; i++)
		{
			if (array[i] == null)
			{
				throw new MissingMemberException("Required crafting/inventory API missing");
			}
		}
	}

	private static void ReadExclusions()
	{
		exclusions.Clear();
		string[] array = ExcludedPrefabs.Value.Split(',');
		foreach (string text in array)
		{
			if (!string.IsNullOrWhiteSpace(text))
			{
				exclusions.Add(text.Trim());
			}
		}
	}

	private static void ExclusionsChanged(object sender, EventArgs args)
	{
		ReadExclusions();
		UpdateSelection(InventoryGui.instance);
		Cache.PanelDirty = true;
	}

	private static void EnabledChanged(object sender, EventArgs args)
	{
		Cache.InvalidateAll();
		ClearManualIntent();
		Cache.PanelDirty = true;
	}

	private void OnGUI()
	{
		OrePicker.Draw();
		SharingUi.Draw();
	}

	private void LateUpdate()
	{
		OrePicker.Tick();
		SharingUi.Tick();
	}

	private void Update()
	{
		SharingService.Tick();
		if (Current != null)
		{
			return;
		}
		try
		{
			Cache.Prune();
			if (wasRunning != Running)
			{
				Cache.InvalidateAll();
				wasRunning = Running;
			}
			if (Running)
			{
				Cache.Poll(all: false);
				if (Cache.PanelDirty && opening == 0 && Active && Object.op_Implicit((Object)(object)InventoryGui.instance) && InventoryGui.IsVisible() && Time.unscaledTime >= nextPanel)
				{
					Cache.PanelDirty = false;
					nextPanel = Time.unscaledTime + 0.15f;
					RefreshPanel.Invoke(InventoryGui.instance, new object[1] { false });
				}
			}
		}
		catch (Exception e)
		{
			Fault("Background resource refresh failed; feature disabled for this session.");
			Warn("Refresh error", e);
		}
	}

	private void OnDestroy()
	{
		try
		{
			Finish(null, ownsScope: true);
		}
		finally
		{
			OrePicker.Close();
			SharingService.Clear();
			installed = false;
			if (Enabled != null)
			{
				Enabled.SettingChanged -= EnabledChanged;
			}
			if (ExcludedPrefabs != null)
			{
				ExcludedPrefabs.SettingChanged -= ExclusionsChanged;
			}
			try
			{
				Harmony obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
			}
			finally
			{
				Cache.Dispose();
				ChestBuilder.Clear();
				requests.Clear();
				ClearManualIntent();
				Instance = null;
			}
		}
	}

	internal static void Warn(string message, Exception e = null)
	{
		if (Object.op_Implicit((Object)(object)Instance))
		{
			((BaseUnityPlugin)Instance).Logger.LogWarning((object)(message + ((e == null) ? "" : (": " + ((e is TargetInvocationException && e.InnerException != null) ? e.InnerException : e)))));
		}
	}

	internal static void Tell(string text)
	{
		if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
		{
			((Character)Player.m_localPlayer).Message((MessageType)2, "Easy craft: " + text, 0, (Sprite)null, false);
		}
	}

	internal static void Fault(string text)
	{
		faulted = true;
		Cache.Clear();
		Warn(text);
		Tell(text);
	}

	internal static void Opening()
	{
		opening++;
		DisplayExcluded = false;
		Cache.PanelDirty = true;
		nextPanel = 0f;
		if (!Running || Current != null)
		{
			return;
		}
		try
		{
			SharingService.RequestUse();
			Cache.Poll(all: true);
		}
		catch (Exception e)
		{
			Fault("Could not prepare chest resources; normal inventory remains available.");
			Warn("Opening error", e);
		}
	}

	internal static void Opened()
	{
		opening = Math.Max(0, opening - 1);
		if (opening != 0 || !Running || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)Player.m_localPlayer.GetCurrentCraftingStation()))
		{
			return;
		}
		int num = 0;
		foreach (Chest chest in Cache.Chests)
		{
			if (chest.Ready)
			{
				num++;
			}
		}
		if (Object.op_Implicit((Object)(object)Instance))
		{
			((BaseUnityPlugin)Instance).Logger.LogInfo((object)("[station] " + ((Object)Player.m_localPlayer.GetCurrentCraftingStation()).name + ": " + num + "/" + Cache.Chests.Count + " loaded chest candidates ready; range=200m; builder-only."));
		}
	}

	internal static void BeginPanel()
	{
		panelDepth++;
		DisplayExcluded = false;
		if (Running && Current == null)
		{
			Cache.Flush();
		}
	}

	internal static void EndPanel(InventoryGui gui)
	{
		try
		{
			UpdateSelection(gui);
		}
		finally
		{
			panelDepth = Math.Max(0, panelDepth - 1);
		}
	}

	internal static bool RecipeAllowed(Recipe recipe)
	{
		if (Object.op_Implicit((Object)(object)recipe) && Object.op_Implicit((Object)(object)recipe.m_item))
		{
			return !exclusions.Contains(((Object)((Component)recipe.m_item).gameObject).name);
		}
		return false;
	}

	internal static void UpdateSelection(InventoryGui gui)
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Expected O, but got Unknown
		if (!Object.op_Implicit((Object)(object)gui) || SelectedGetter == null)
		{
			DisplayExcluded = false;
			return;
		}
		try
		{
			DisplayExcluded = !RecipeAllowed((Recipe)SelectedGetter.Invoke(SelectedField.GetValue(gui), null));
		}
		catch (Exception e)
		{
			DisplayExcluded = true;
			Warn("Custom recipe selection could not be identified; using inventory only", e);
		}
	}

	internal static void Prepare(Chest c)
	{
		if (!Running || !SharingService.CanSpend(c))
		{
			return;
		}
		if (requests.TryGetValue(c.Container, out var value))
		{
			float num = Time.unscaledTime - value.Sent;
			float num2 = ((value.Owner != c.View.GetZDO().GetOwner()) ? 0.5f : (value.TimedOut ? 2f : 8f));
			if (num < (value.Completed ? 2f : num2))
			{
				return;
			}
		}
		if (Time.unscaledTime >= requestWindow)
		{
			requestWindow = Time.unscaledTime + 1f;
			sentInWindow = 0;
		}
		if (sentInWindow < 4)
		{
			sentInWindow++;
			requests[c.Container] = new Request
			{
				Owner = c.View.GetZDO().GetOwner(),
				Sent = Time.unscaledTime
			};
			c.View.InvokeRPC(c.View.GetZDO().GetOwner(), "RPC_RequestOpen", new object[1] { Player.m_localPlayer.GetPlayerID() });
		}
	}

	internal static bool AccessReady(Chest c)
	{
		if (!SharingService.CanSpend(c))
		{
			return false;
		}
		if (!requests.TryGetValue(c.Container, out var value))
		{
			return true;
		}
		if (value.Completed)
		{
			return c.View.IsOwner();
		}
		if (Time.unscaledTime - value.Sent >= 8f)
		{
			value.TimedOut = true;
		}
		if (c.View.IsOwner() && (value.Granted || value.TimedOut))
		{
			value.Completed = true;
			return true;
		}
		return false;
	}

	internal static void ClearManualIntent()
	{
		manual = null;
		manualPlayer = 0L;
		manualOwner = 0L;
		manualUntil = 0f;
	}

	internal static void ForgetRequest(Container c)
	{
		requests.Remove(c);
	}

	internal static void ManualOpen(Container c)
	{
		manual = c;
		manualPlayer = (Object.op_Implicit((Object)(object)Player.m_localPlayer) ? Player.m_localPlayer.GetPlayerID() : 0);
		manualUntil = Time.unscaledTime + 8f;
		ZNetView val = (Object.op_Implicit((Object)(object)c) ? StorageIdentity.View(c) : null);
		manualOwner = ((Object.op_Implicit((Object)(object)val) && val.IsValid()) ? val.GetZDO().GetOwner() : 0);
	}

	internal static bool Response(Container c, long sender, bool granted)
	{
		if (!requests.TryGetValue(c, out var value))
		{
			return true;
		}
		Cache.ByContainer.TryGetValue(c, out var value2);
		if (sender == value.Owner)
		{
			value.Granted = granted;
			if (!granted)
			{
				value.TimedOut = true;
				value.Completed = false;
			}
		}
		bool flag = (Object)(object)manual == (Object)(object)c && Time.unscaledTime <= manualUntil && Object.op_Implicit((Object)(object)Player.m_localPlayer) && Player.m_localPlayer.GetPlayerID() == manualPlayer && (sender == value.Owner || sender == manualOwner);
		if (flag && granted)
		{
			value.Completed = true;
			value.TimedOut = false;
			ClearManualIntent();
			if (value2 != null)
			{
				Cache.InventoryDirty(value2.Inventory);
			}
			return true;
		}
		if (value2 != null)
		{
			Cache.Dirty(value2);
		}
		if (flag && !granted)
		{
			Tell("Chest is busy or access was denied.");
		}
		return false;
	}

	private static bool Combine(Inventory inventory)
	{
		if (Running && Object.op_Implicit((Object)(object)Player.m_localPlayer) && inventory == ((Humanoid)Player.m_localPlayer).GetInventory())
		{
			if (BuildDisplayDepth <= 0)
			{
				if (SuppressCount == 0)
				{
					return Active;
				}
				return false;
			}
			return true;
		}
		return false;
	}

	public static int Count(Inventory inventory, string name, int quality, bool world)
	{
		int num = inventory.CountItems(name, quality, world);
		if (!Combine(inventory))
		{
			return num;
		}
		checked
		{
			if (Current == null)
			{
				return num + Cache.Count(name, quality, world);
			}
			foreach (Chest source in Current.Sources)
			{
				num += source.Inventory.CountItems(name, quality, world);
			}
			return num;
		}
	}

	public static bool HaveItem(Inventory inventory, string name, bool world)
	{
		return Count(inventory, name, -1, world) > 0;
	}

	public static ItemData GetItem(Inventory inventory, string name, int quality, bool prefab)
	{
		ItemData item = inventory.GetItem(name, quality, prefab);
		if (item != null || !Combine(inventory) || prefab)
		{
			return item;
		}
		if (Current == null)
		{
			return Cache.First(name, quality);
		}
		foreach (Chest source in Current.Sources)
		{
			item = source.Inventory.GetItem(name, quality, prefab);
			if (item != null && item.m_worldLevel >= Game.m_worldLevel)
			{
				return item;
			}
		}
		return null;
	}

	public static void Remove(Inventory inventory, string name, int amount, int quality, bool world)
	{
		if (Current == null || inventory != ((Humanoid)Current.Player).GetInventory())
		{
			inventory.RemoveItem(name, amount, quality, world);
		}
		else
		{
			Current.Remove(inventory, name, amount, quality, world);
		}
	}

	public static bool ItemCheated(Inventory inventory, Requirement[] requirements, int quality, bool world)
	{
		if (inventory.ItemCheated(requirements, quality, world))
		{
			return true;
		}
		if (Current == null)
		{
			if (PlacementScope > 0 && Combine(inventory))
			{
				return Cache.AnyCheated(requirements, quality, world);
			}
			return false;
		}
		foreach (CraftTransaction.Debit debit in Current.Debits)
		{
			if (debit.Item.m_cheated)
			{
				return true;
			}
		}
		return false;
	}

	public static ItemData AddCrafted(Inventory inventory, string name, int amount, int quality, int variant, long creator, string creatorName, Vector2i pos, bool cheated, bool pickedUp, bool dropIfFull)
	{
		//IL_005d: 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)
		if (Current == null)
		{
			return inventory.AddItem(name, amount, quality, variant, creator, creatorName, pos, cheated, pickedUp, dropIfFull);
		}
		string name2 = Current.Recipe.m_item.m_itemData.m_shared.m_name;
		int num = inventory.CountItems(name2, quality, false);
		Current.Mutated = true;
		ItemData obj = inventory.AddItem(name, amount, quality, variant, creator, creatorName, pos, cheated, pickedUp, false);
		if (obj == null || inventory.CountItems(name2, quality, false) - num != amount)
		{
			throw new InvalidOperationException("Crafted output could not be inserted completely; recovering inventories");
		}
		return obj;
	}

	public static bool BeginNative(InventoryGui gui, Player player)
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Expected O, but got Unknown
		if (!Active || (Object)(object)player != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)player.GetCurrentCraftingStation()))
		{
			return true;
		}
		if (Current != null)
		{
			Tell("Another resource transaction is active.");
			return false;
		}
		Recipe val = (Recipe)RecipeField.GetValue(gui);
		if (!RecipeAllowed(val) || player.NoCostCheat() || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25))
		{
			return true;
		}
		try
		{
			Cache.Poll(all: true);
			object? value = UpgradeField.GetValue(gui);
			ItemData val2 = (ItemData)((value is ItemData) ? value : null);
			int num = ((val2 == null) ? 1 : (val2.m_quality + 1));
			int num2 = ((!(bool)MultiField.GetValue(gui)) ? 1 : ((int)AmountField.GetValue(gui)));
			if (num2 < 1 || !player.HaveRequirements(val, false, num, num2))
			{
				return false;
			}
			CraftTransaction craftTransaction = new CraftTransaction(player, val);
			if (val.m_requireOnlyOneIngredient)
			{
				int amount = default(int);
				int num3 = default(int);
				ItemData firstRequiredItem = player.GetFirstRequiredItem(((Humanoid)player).GetInventory(), val, num, ref amount, ref num3, num2);
				if (firstRequiredItem == null)
				{
					return false;
				}
				craftTransaction.Plan(new ResourceKey(firstRequiredItem.m_shared.m_name, firstRequiredItem.m_quality, world: true), amount);
			}
			else
			{
				Requirement[] resources = val.m_resources;
				foreach (Requirement val3 in resources)
				{
					if (Object.op_Implicit((Object)(object)val3.m_resItem) && val3.m_upgraderResource == player.GetCurrentCraftingStation().m_upgrader)
					{
						craftTransaction.Plan(new ResourceKey(val3.m_resItem.m_itemData.m_shared.m_name, -1, world: true), checked(val3.GetAmount(num) * num2));
					}
				}
			}
			if (craftTransaction.Sources.Count == 0)
			{
				return true;
			}
			Current = craftTransaction;
			craftTransaction.Acquire();
			return true;
		}
		catch (Exception ex)
		{
			Warn("Craft preflight rejected", ex);
			Finish(ex, ownsScope: true);
			Tell("Materials or chest access changed. Please try again.");
			return false;
		}
	}

	public static void CommitAndRefresh(InventoryGui gui, bool focus)
	{
		if (Current != null)
		{
			Current.Commit();
		}
		RefreshPanel.Invoke(gui, new object[1] { focus });
	}

	internal static bool InventoryChanging(Inventory inventory)
	{
		CraftTransaction current = Current;
		if (current == null || !current.Manages(inventory) || current.Publishing || current.Committed)
		{
			return true;
		}
		current.Mutated = true;
		return false;
	}

	internal static bool ContainerChanged(Container c)
	{
		if (Current != null && !Current.Committed && !Current.Publishing)
		{
			return !Current.Manages(c.GetInventory());
		}
		return true;
	}

	internal static Exception Finish(Exception error, bool ownsScope)
	{
		if (!ownsScope || Current == null)
		{
			return error;
		}
		CraftTransaction current = Current;
		try
		{
			if (!current.Committed)
			{
				bool flag = current.Rollback();
				if (error != null)
				{
					Warn(flag ? "Operation failed; inventory state restored" : "Operation failed; recovery uncertain", error);
					Tell(flag ? "Operation cancelled; materials restored." : "Recovery is uncertain. Check the log before continuing.");
				}
			}
			else if (error != null)
			{
				Warn("Paid operation completed; later feedback failed (not rolled back)", error);
			}
		}
		catch (Exception e)
		{
			Fault("Unexpected recovery error. Reload before using Easy craft again.");
			Warn("Recovery error", e);
		}
		finally
		{
			try
			{
				current.Release();
			}
			finally
			{
				Current = null;
				Cache.PanelDirty = true;
			}
		}
		return null;
	}
}
internal static class BuildWindowRefresh
{
	private static readonly FieldInfo Buttons = AccessTools.Field(typeof(BuildUi), "m_pieceButtons");

	private static readonly FieldInfo Special = AccessTools.Field(typeof(BuildUi), "m_specialPieceButton");

	private static object lastWindow;

	private static int revision = -1;

	private static float nextRefresh;

	internal static void Opened(object window)
	{
		revision = -1;
		nextRefresh = 0f;
		Tick(window);
	}

	internal static void Tick(object __instance)
	{
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Expected O, but got Unknown
		if (!Plugin.Running || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !((Character)Player.m_localPlayer).InPlaceMode())
		{
			return;
		}
		if (lastWindow != __instance)
		{
			lastWindow = __instance;
			revision = -1;
		}
		if (revision == Plugin.Cache.Revision || Time.unscaledTime < nextRefresh)
		{
			return;
		}
		if (Buttons.GetValue(__instance) is IEnumerable enumerable)
		{
			foreach (BuildUiPieceButton item in enumerable)
			{
				BuildUiPieceButton val = item;
				if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.Piece))
				{
					val.UpdateRequirements();
				}
			}
		}
		object? value = Special.GetValue(__instance);
		BuildUiPieceButton val2 = (BuildUiPieceButton)((value is BuildUiPieceButton) ? value : null);
		if (Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.Piece))
		{
			val2.UpdateRequirements();
		}
		revision = Plugin.Cache.Revision;
		nextRefresh = Time.unscaledTime + 0.1f;
	}
}
internal struct ResourceKey : IEquatable<ResourceKey>
{
	internal string Name;

	internal int Quality;

	internal bool World;

	internal ResourceKey(string name, int quality, bool world)
	{
		Name = name;
		Quality = quality;
		World = world;
	}

	public bool Equals(ResourceKey other)
	{
		if (Name == other.Name && Quality == other.Quality)
		{
			return World == other.World;
		}
		return false;
	}

	public override bool Equals(object obj)
	{
		if (obj is ResourceKey)
		{
			return Equals((ResourceKey)obj);
		}
		return false;
	}

	public override int GetHashCode()
	{
		return ((((Name != null) ? Name.GetHashCode() : 0) * 397) ^ Quality) * 31 + (World ? 1 : 0);
	}
}
internal sealed class Chest
{
	internal Container Container;

	internal ZNetView View;

	internal Piece Piece;

	internal Inventory Inventory;

	internal bool Ready;

	internal bool Broken;

	internal bool Pending;

	internal bool Dirty = true;

	internal float NextScan;

	internal float NextSummary;

	internal byte[] Bytes;

	internal Dictionary<ResourceKey, int> Counts = new Dictionary<ResourceKey, int>();

	internal Dictionary<ResourceKey, int> NextCounts = new Dictionary<ResourceKey, int>();

	internal Dictionary<ResourceKey, ItemData> First = new Dictionary<ResourceKey, ItemData>();

	internal Dictionary<ResourceKey, ItemData> NextFirst = new Dictionary<ResourceKey, ItemData>();

	internal readonly HashSet<ResourceKey> Cheated = new HashSet<ResourceKey>();

	internal Chest(Container c)
	{
		Container = c;
		View = StorageIdentity.View(c);
		Piece = StorageIdentity.Piece(c);
		Inventory = c.GetInventory();
	}
}
internal sealed class ResourceCache
{
	private sealed class Bucket
	{
		internal int Count;

		internal readonly HashSet<Chest> Sources = new HashSet<Chest>();
	}

	internal readonly List<Chest> Chests = new List<Chest>();

	internal readonly Dictionary<Container, Chest> ByContainer = new Dictionary<Container, Chest>();

	internal readonly Dictionary<Inventory, Chest> ByInventory = new Dictionary<Inventory, Chest>();

	private readonly Dictionary<ResourceKey, Bucket> totals = new Dictionary<ResourceKey, Bucket>();

	private readonly HashSet<Chest> dirty = new HashSet<Chest>();

	private readonly List<Chest> work = new List<Chest>();

	private int cursor;

	private int pruneCursor;

	private int world = int.MinValue;

	private long player;

	private bool flushing;

	private bool panelDirty;

	private readonly HashSet<Chest> forceSummaries = new HashSet<Chest>();

	internal int Revision { get; private set; }

	internal bool PanelDirty
	{
		get
		{
			return panelDirty;
		}
		set
		{
			panelDirty = value;
			if (value)
			{
				int revision = Revision + 1;
				Revision = revision;
			}
		}
	}

	internal void Add(Container c)
	{
		if (Object.op_Implicit((Object)(object)c) && !ByContainer.ContainsKey(c))
		{
			Chest chest = new Chest(c);
			if (chest.Inventory != null && Object.op_Implicit((Object)(object)chest.View) && Object.op_Implicit((Object)(object)chest.Piece))
			{
				Chests.Add(chest);
				ByContainer[c] = chest;
				ByInventory[chest.Inventory] = chest;
				Dirty(chest);
			}
		}
	}

	internal void Dirty(Chest c)
	{
		c.Dirty = true;
		dirty.Add(c);
	}

	internal void Changed(Inventory inventory)
	{
		if (ByInventory.TryGetValue(inventory, out var value))
		{
			Dirty(value);
		}
	}

	internal void Loaded(Container container)
	{
		if (ByContainer.TryGetValue(container, out var value))
		{
			forceSummaries.Add(value);
			Dirty(value);
		}
	}

	private void Subtract(Chest c)
	{
		if (!c.Ready)
		{
			return;
		}
		foreach (KeyValuePair<ResourceKey, int> count in c.Counts)
		{
			if (totals.TryGetValue(count.Key, out var value))
			{
				value.Count -= count.Value;
				value.Sources.Remove(c);
				if (value.Sources.Count == 0)
				{
					totals.Remove(count.Key);
				}
			}
		}
		c.Ready = false;
		PanelDirty = true;
	}

	internal void Clear()
	{
		foreach (Chest chest in Chests)
		{
			chest.Ready = false;
			chest.Pending = false;
			chest.Counts.Clear();
			chest.First.Clear();
			chest.Dirty = true;
		}
		totals.Clear();
		dirty.Clear();
		forceSummaries.Clear();
		PanelDirty = true;
	}

	internal void Dispose()
	{
		Clear();
		Chests.Clear();
		ByContainer.Clear();
		ByInventory.Clear();
		work.Clear();
	}

	internal void Prune()
	{
		int num = Math.Min(16, Chests.Count);
		for (int i = 0; i < num; i++)
		{
			if (Chests.Count <= 0)
			{
				break;
			}
			if (pruneCursor >= Chests.Count)
			{
				pruneCursor = 0;
			}
			Chest chest = Chests[pruneCursor];
			if (Object.op_Implicit((Object)(object)chest.Container))
			{
				pruneCursor++;
				continue;
			}
			Subtract(chest);
			dirty.Remove(chest);
			forceSummaries.Remove(chest);
			ByContainer.Remove(chest.Container);
			ByInventory.Remove(chest.Inventory);
			Plugin.ForgetRequest(chest.Container);
			Chests.RemoveAt(pruneCursor);
		}
	}

	internal void InvalidateAll()
	{
		Clear();
		foreach (Chest chest in Chests)
		{
			Dirty(chest);
		}
	}

	internal bool Usable(Chest c, bool reserved = false)
	{
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if (!Plugin.Running || !Object.op_Implicit((Object)(object)localPlayer) || !Object.op_Implicit((Object)(object)c.Container) || c.Broken || !((Component)c.Container).gameObject.activeInHierarchy || !Object.op_Implicit((Object)(object)c.View) || !c.View.IsValid() || !Object.op_Implicit((Object)(object)c.Piece))
		{
			return false;
		}
		if (!SharingService.CanUse(c) || (reserved && !SharingService.CanSpend(c)))
		{
			return false;
		}
		Vector3 val = ((Component)c.Container).transform.position - ((Component)localPlayer).transform.position;
		if (((Vector3)(ref val)).sqrMagnitude > 40000f)
		{
			return false;
		}
		_ = c.Container;
		if (!Object.op_Implicit((Object)null))
		{
			_ = c.Container;
			if (!Object.op_Implicit((Object)null) && !Object.op_Implicit((Object)(object)((Component)c.Container).GetComponent<TombStone>()))
			{
				if (!(bool)Plugin.Access.Invoke(c.Container, new object[1] { localPlayer.GetPlayerID() }))
				{
					return false;
				}
				if (c.Container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)c.Container).transform.position, 0f, false, false))
				{
					return false;
				}
				if (!reserved && (c.Container.IsInUse() || c.View.GetZDO().GetInt(ZDOVars.s_inUse, 0) != 0))
				{
					return false;
				}
				return true;
			}
		}
		return false;
	}

	internal void Poll(bool all)
	{
		long num = (Object.op_Implicit((Object)(object)Player.m_localPlayer) ? Player.m_localPlayer.GetPlayerID() : 0);
		if (num != player || world != Game.m_worldLevel)
		{
			ChestBuilder.Clear();
			player = num;
			world = Game.m_worldLevel;
			InvalidateAll();
			Plugin.ClearManualIntent();
		}
		if (!Plugin.Running || num == 0L)
		{
			if (totals.Count != 0)
			{
				Clear();
			}
			return;
		}
		int num2 = (all ? Chests.Count : Math.Min(16, Chests.Count));
		for (int i = 0; i < num2; i++)
		{
			if (Chests.Count <= 0)
			{
				break;
			}
			if (cursor >= Chests.Count)
			{
				cursor = 0;
			}
			Chest chest = Chests[cursor];
			if (!Object.op_Implicit((Object)(object)chest.Container))
			{
				Subtract(chest);
				dirty.Remove(chest);
				ByContainer.Remove(chest.Container);
				ByInventory.Remove(chest.Inventory);
				Plugin.ForgetRequest(chest.Container);
				Chests.RemoveAt(cursor);
				continue;
			}
			cursor++;
			if (all)
			{
				forceSummaries.Add(chest);
			}
			if (all || Time.unscaledTime >= chest.NextScan)
			{
				Dirty(chest);
			}
		}
		Flush(all ? int.MaxValue : 16);
	}

	internal void Flush(int limit = 16)
	{
		if (flushing || Plugin.Current != null || !Plugin.Running || dirty.Count == 0)
		{
			return;
		}
		flushing = true;
		try
		{
			work.Clear();
			foreach (Chest item in dirty)
			{
				work.Add(item);
				if (work.Count >= limit)
				{
					break;
				}
			}
			foreach (Chest item2 in work)
			{
				dirty.Remove(item2);
				item2.Dirty = false;
				try
				{
					Refresh(item2);
				}
				catch (Exception e)
				{
					Subtract(item2);
					item2.Broken = true;
					Plugin.Warn("Chest skipped after a load/access error", e);
				}
			}
		}
		finally
		{
			work.Clear();
			flushing = false;
		}
	}

	internal void Refresh(Chest c)
	{
		c.NextScan = Time.unscaledTime + ((Plugin.Active || OrePicker.Visible) ? 0.2f : 0.5f);
		ChestBuilder.Remember(c.Container);
		bool flag = (c.Pending = Usable(c));
		if (flag && !c.View.IsOwner())
		{
			Plugin.Prepare(c);
		}
		if (!flag)
		{
			Subtract(c);
			return;
		}
		c.Pending = !c.View.IsOwner() || !Plugin.AccessReady(c);
		byte[] byteArray = c.View.GetZDO().GetByteArray(ZDOVars.s_items, (byte[])null);
		if (!c.View.IsOwner() && byteArray == null)
		{
			Subtract(c);
			return;
		}
		bool flag2 = byteArray != c.Bytes;
		if (flag2)
		{
			Plugin.Load.Invoke(c.Container, null);
			c.Bytes = byteArray;
		}
		if (c.Ready && !c.Dirty && !flag2 && !forceSummaries.Contains(c) && Time.unscaledTime < c.NextSummary)
		{
			return;
		}
		forceSummaries.Remove(c);
		c.NextSummary = Time.unscaledTime + (Plugin.Active ? 2f : 5f);
		c.NextCounts.Clear();
		c.NextFirst.Clear();
		c.Cheated.Clear();
		foreach (ItemData allItem in c.Inventory.GetAllItems())
		{
			if (allItem == null || allItem.m_shared == null || allItem.m_stack <= 0)
			{
				throw new InvalidOperationException("Invalid chest item");
			}
			AddItem(c, allItem, allItem.m_quality, world: false);
			AddItem(c, allItem, -1, world: false);
			if (allItem.m_worldLevel >= Game.m_worldLevel)
			{
				AddItem(c, allItem, allItem.m_quality, world: true);
				AddItem(c, allItem, -1, world: true);
			}
		}
		bool flag3 = c.Ready && c.Counts.Count == c.NextCounts.Count;
		if (flag3)
		{
			foreach (KeyValuePair<ResourceKey, int> count in c.Counts)
			{
				if (!c.NextCounts.TryGetValue(count.Key, out var value) || count.Value != value)
				{
					flag3 = false;
					break;
				}
			}
		}
		if (!flag3)
		{
			Subtract(c);
		}
		Dictionary<ResourceKey, int> counts = c.Counts;
		c.Counts = c.NextCounts;
		c.NextCounts = counts;
		Dictionary<ResourceKey, ItemData> first = c.First;
		c.First = c.NextFirst;
		c.NextFirst = first;
		if (flag3)
		{
			return;
		}
		checked
		{
			foreach (KeyValuePair<ResourceKey, int> count2 in c.Counts)
			{
				if (!totals.TryGetValue(count2.Key, out var value2))
				{
					value2 = new Bucket();
					totals.Add(count2.Key, value2);
				}
				value2.Count += count2.Value;
				value2.Sources.Add(c);
			}
			c.Ready = true;
			PanelDirty = true;
		}
	}

	internal void InventoryDirty(Inventory inventory)
	{
		if (ByInventory.TryGetValue(inventory, out var value))
		{
			forceSummaries.Add(value);
			Dirty(value);
		}
		else if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && inventory == ((Humanoid)Player.m_localPlayer).GetInventory())
		{
			PanelDirty = true;
		}
	}

	private static void AddItem(Chest c, ItemData item, int quality, bool world)
	{
		ResourceKey resourceKey = new ResourceKey(item.m_shared.m_name, quality, world);
		c.NextCounts.TryGetValue(resourceKey, out var value);
		c.NextCounts[resourceKey] = checked(value + item.m_stack);
		if (!c.NextFirst.ContainsKey(resourceKey))
		{
			c.NextFirst[resourceKey] = item;
		}
		if (item.m_cheated)
		{
			c.Cheated.Add(resourceKey);
		}
	}

	internal int Count(string name, int quality, bool worldLevel)
	{
		if (!totals.TryGetValue(new ResourceKey(name, quality, worldLevel), out var value))
		{
			return 0;
		}
		return value.Count;
	}

	internal ItemData First(string name, int quality)
	{
		ResourceKey key = new ResourceKey(name, quality, world: true);
		if (totals.TryGetValue(key, out var value))
		{
			foreach (Chest source in value.Sources)
			{
				if (source.First.TryGetValue(key, out var value2))
				{
					return value2;
				}
			}
		}
		return null;
	}

	internal bool AnyCheated(Requirement[] resources, int quality, bool worldLevel)
	{
		foreach (Requirement val in resources)
		{
			if (!Object.op_Implicit((Object)(object)val.m_resItem))
			{
				continue;
			}
			ResourceKey resourceKey = new ResourceKey(val.m_resItem.m_itemData.m_shared.m_name, quality, worldLevel);
			if (!totals.TryGetValue(resourceKey, out var value))
			{
				continue;
			}
			foreach (Chest source in value.Sources)
			{
				if (source.Cheated.Contains(resourceKey))
				{
					return true;
				}
			}
		}
		return false;
	}
}
internal static class ChestBuilder
{
	internal const string NameKey = "easycraft_builder_name_v1";

	internal const string IdKey = "easycraft_builder_id_v1";

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

	private static Container lastChest;

	private static long lastCreator;

	private static string lastLine;

	private static float nextLookup;

	private static bool warned;

	internal static void Clear()
	{
		names.Clear();
		lastChest = null;
		lastLine = null;
		nextLookup = 0f;
		warned = false;
	}

	internal static string Clean(string name)
	{
		if (string.IsNullOrWhiteSpace(name))
		{
			return null;
		}
		name = name.Replace('<', '‹').Replace('>', '›').Replace('\r', ' ')
			.Replace('\n', ' ')
			.Replace('\t', ' ')
			.Trim();
		if (name.Length <= 80)
		{
			return name;
		}
		return name.Substring(0, 80);
	}

	internal static string Resolve(Piece piece, ZNetView view)
	{
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		long creator = piece.GetCreator();
		if (creator == 0L)
		{
			return null;
		}
		string value = null;
		Player val = Player.m_localPlayer;
		if (!Object.op_Implicit((Object)(object)val) || val.GetPlayerID() != creator)
		{
			val = Player.GetPlayer(creator);
		}
		if (Object.op_Implicit((Object)(object)val))
		{
			value = Clean(val.GetPlayerName());
		}
		ZDO val2 = ((Object.op_Implicit((Object)(object)view) && view.IsValid()) ? view.GetZDO() : null);
		if (value == null && val2 != null && val2.GetLong("easycraft_builder_id_v1", 0L) == creator)
		{
			value = Clean(val2.GetString("easycraft_builder_name_v1", ""));
		}
		if (value == null)
		{
			names.TryGetValue(creator, out value);
		}
		if (value == null && val2 != null)
		{
			int num = val2.GetInt(ZDOVars.s_creatorIndex, -1);
			List<CrossNetworkUserInfo> list = ((!Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.GetWorld() == null) ? null : ZNet.instance.GetWorld().m_playerHistory);
			if (num >= 0 && list != null && num < list.Count)
			{
				string text = Clean(list[num].m_displayName);
				if (text != null)
				{
					value = ((text.Length > 68) ? text.Substring(0, 68) : text) + " (account)";
				}
			}
		}
		if (value == null)
		{
			return null;
		}
		if (names.Count >= 4096 && !names.ContainsKey(creator))
		{
			names.Clear();
		}
		names[creator] = value;
		if (val2 != null && view.IsOwner() && (val2.GetLong("easycraft_builder_id_v1", 0L) != creator || val2.GetString("easycraft_builder_name_v1", "") != value))
		{
			val2.Set("easycraft_builder_name_v1", value);
			val2.Set("easycraft_builder_id_v1", creator);
		}
		return value;
	}

	internal static void Remember(Container chest)
	{
		try
		{
			if (Object.op_Implicit((Object)(object)chest) && !Object.op_Implicit((Object)(object)((Component)chest).GetComponent<TombStone>()))
			{
				Piece val = StorageIdentity.Piece(chest);
				if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)Player.m_localPlayer) && val.GetCreator() == Player.m_localPlayer.GetPlayerID())
				{
					Resolve(val, StorageIdentity.View(chest));
				}
			}
		}
		catch (Exception e)
		{
			Report(e);
		}
	}

	internal static void Created(Piece piece)
	{
		if (Object.op_Implicit((Object)(object)piece))
		{
			Container component = ((Component)piece).GetComponent<Container>();
			if (Object.op_Implicit((Object)(object)component))
			{
				Remember(component);
			}
			lastChest = null;
		}
	}

	internal static string Hover(Container chest, string original)
	{
		if (!Plugin.Running || !Object.op_Implicit((Object)(object)chest) || Object.op_Implicit((Object)(object)((Component)chest).GetComponent<TombStone>()))
		{
			return original;
		}
		try
		{
			Piece val = StorageIdentity.Piece(chest);
			long num = (Object.op_Implicit((Object)(object)val) ? val.GetCreator() : 0);
			if ((Object)(object)lastChest != (Object)(object)chest || lastCreator != num || Time.unscaledTime >= nextLookup)
			{
				lastChest = chest;
				lastCreator = num;
				nextLookup = Time.unscaledTime + 1f;
				string text = ((Object.op_Implicit((Object)(object)val) && num != 0L) ? Resolve(val, StorageIdentity.View(chest)) : null);
				string text2 = ((num == 0L) ? "World / no recorded builder" : (text ?? "Unknown player"));
				bool flag = num != 0L && Object.op_Implicit((Object)(object)Player.m_localPlayer) && num == Player.m_localPlayer.GetPlayerID();
				lastLine = "Built by: <color=#FFD479>" + text2 + "</color>" + (flag ? " (you)" : "");
			}
			return original + "\n" + lastLine;
		}
		catch (Exception e)
		{
			Report(e);
			return original;
		}
	}

	private static void Report(Exception e)
	{
		if (!warned)
		{
			warned = true;
			Plugin.Warn("Chest builder label unavailable; normal chest behaviour preserved", e);
		}
	}
}
internal static class HoeStock
{
	internal static void Show(object hudObject, Piece piece)
	{
		Hud val = (Hud)((hudObject is Hud) ? hudObject : null);
		if (!Plugin.Running || !Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)piece) || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)val.m_pieceDescription))
		{
			return;
		}
		HashSet<string> hashSet = new HashSet<string>();
		List<string> list = new List<string>();
		Requirement[] resources = piece.m_resources;
		foreach (Requirement val2 in resources)
		{
			ItemDrop resItem = val2.m_resItem;
			if (Object.op_Implicit((Object)(object)resItem) && val2.GetAmount(0) > 0 && hashSet.Add(resItem.m_itemData.m_shared.m_name))
			{
				int num = Plugin.BuildCount(((Humanoid)Player.m_localPlayer).GetInventory(), resItem.m_itemData.m_shared.m_name, -1, world: true);
				string text = Localization.instance.Localize(resItem.m_itemData.m_shared.m_name);
				list.Add(text + ": " + num);
			}
		}
		if (list.Count > 0)
		{
			TMP_Text pieceDescription = val.m_pieceDescription;
			pieceDescription.text = pieceDescription.text + "\n<color=#FFD479><b>Available — " + string.Join("  •  ", list.ToArray()) + "</b></color>";
		}
	}
}
internal static class OrePicker
{
	private static Smelter station;

	private static Switch input;

	private static List<ItemDrop> allChoices = new List<ItemDrop>();

	private static readonly List<ItemDrop> choices = new List<ItemDrop>();

	private static readonly List<int> counts = new List<int>();

	private static int selected;

	private static int quantityIndex;

	private static readonly int[] oreQuantities = new int[3] { 1, 5, 10 };

	private static readonly int[] woodQuantities = new int[4] { 1, 5, 10, 25 };

	private static int[] quantities = oreQuantities;

	private static readonly Dictionary<Smelter, QueueCapacity> capacities = new Dictionary<Smelter, QueueCapacity>();

	private static bool sending;

	private static float readyAt;

	private static float nextRefresh;

	private static Vector2 scroll;

	private static CursorLockMode oldLock;

	private static bool oldVisible;

	private static string status = "Choose an available material.";

	private static GUIStyle textStyle;

	private static GUIStyle smallStyle;

	private static GUIStyle titleStyle;

	private static GUIStyle rowStyle;

	private static GUIStyle buttonStyle;

	private static GUIStyle emptyStyle;

	private static GUIStyle countStyle;

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

	private static readonly Dictionary<ItemDrop, Sprite> icons = new Dictionary<ItemDrop, Sprite>();

	private const int RowHeight = 66;

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

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

	private static bool syncing;

	private static string stationStock = "";

	private static bool opened
	{
		get
		{
			return PickerInput.Open;
		}
		set
		{
			PickerInput.Open = value;
		}
	}

	internal static bool Visible => opened;

	internal static bool Capturing => PickerInput.Capturing;

	internal static bool BlockInput(ref bool __result)
	{
		return PickerInput.BlockPlayerInput(ref __result);
	}

	internal static bool Open(Smelter __instance, Switch sw, Humanoid user, ItemData item, ref bool __result)
	{
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		if (sending || !Plugin.Running || (Object)(object)user != (Object)(object)Player.m_localPlayer || item != null)
		{
			return true;
		}
		string text = ((Object)((Component)__instance).gameObject).name.Replace("(Clone)", "").Trim();
		if (text != "smelter" && text != "blastfurnace" && text != "charcoal_kiln")
		{
			return true;
		}
		quantities = ((text == "charcoal_kiln") ? woodQuantities : oreQuantities);
		List<ItemDrop> list = (from x in Processors.Inputs(__instance)
			group x by x.m_itemData.m_shared.m_name into x
			select x.First()).ToList();
		if (list.Count < 2)
		{
			return true;
		}
		__result = false;
		if (Capturing)
		{
			return false;
		}
		SharingService.RequestUse();
		Plugin.Cache.Poll(all: true);
		opened = true;
		station = __instance;
		input = sw;
		allChoices = list;
		selected = 0;
		quantityIndex = 0;
		scroll = Vector2.zero;
		oldLock = Cursor.lockState;
		oldVisible = Cursor.visible;
		readyAt = Time.unscaledTime + 0.25f;
		nextRefresh = 0f;
		status = "";
		names.Clear();
		icons.Clear();
		foreach (ItemDrop allChoice in allChoices)
		{
			names[allChoice] = Localization.instance.Localize(allChoice.m_itemData.m_shared.m_name);
			icons[allChoice] = allChoice.m_itemData.GetIcon();
		}
		RefreshChoices();
		ReleaseCursor();
		return false;
	}

	internal static void ReleaseCursor()
	{
		SharingUi.ReleaseCursor();
		if (Visible)
		{
			Cursor.lockState = (CursorLockMode)0;
			Cursor.visible = !ZInput.IsGamepadActive();
		}
	}

	internal static void Close()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		if (Visible)
		{
			Cursor.lockState = oldLock;
			Cursor.visible = oldVisible;
			PickerInput.BlockUntil = Time.unscaledTime + 0.2f;
		}
		opened = false;
		station = null;
		input = null;
		allChoices.Clear();
		choices.Clear();
		counts.Clear();
		names.Clear();
		icons.Clear();
	}

	private static int Available(ItemDrop from)
	{
		return Processors.Count(((Humanoid)Player.m_localPlayer).GetInventory(), from.m_itemData.m_shared.m_name, -1, world: true);
	}

	private static void RefreshChoices()
	{
		int current = (int)Queue.Invoke(station, null);
		stationStock = "In station: " + current + " / " + station.m_maxOre;
		if (capacities.TryGetValue(station, out var value))
		{
			value.Free(current, station.m_maxOre, Time.unscaledTime);
			if (value.Pending == 0 && status == "Waiting for station update.")
			{
				status = "";
			}
			if (value.Pending > 0)
			{
				stationStock = stationStock + "  (+" + value.Pending + " syncing)";
			}
		}
		string keep = ((selected >= 0 && selected < choices.Count) ? choices[selected].m_itemData.m_shared.m_name : null);
		Plugin.Cache.Poll(all: false);
		syncing = Plugin.Cache.Chests.Any((Chest c) => c.Pending);
		choices.Clear();
		counts.Clear();
		foreach (ItemDrop allChoice in allChoices)
		{
			int num = Available(allChoice);
			if (num > 0)
			{
				choices.Add(allChoice);
				counts.Add(num);
			}
		}
		int num2 = choices.FindIndex((ItemDrop x) => x.m_itemData.m_shared.m_name == keep);
		selected = ((num2 >= 0) ? num2 : Math.Max(0, Math.Min(selected, choices.Count - 1)));
		scroll.y = Mathf.Clamp(scroll.y, 0f, (float)Math.Max(0, (choices.Count - 5) * 66));
		nextRefresh = Time.unscaledTime + 0.2f;
	}

	internal static void Tick()
	{
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		if (!Visible)
		{
			return;
		}
		if (Object.op_Implicit((Object)(object)station) && Plugin.Running && Object.op_Implicit((Object)(object)Player.m_localPlayer) && !((Character)Player.m_localPlayer).IsDead())
		{
			Vector3 val = ((Component)Player.m_localPlayer).transform.position - ((Component)station).transform.position;
			if (!(((Vector3)(ref val)).sqrMagnitude > 25f))
			{
				ReleaseCursor();
				if (Time.unscaledTime >= nextRefresh)
				{
					RefreshChoices();
				}
				if (Time.unscaledTime < readyAt)
				{
					return;
				}
				PickerInput.Reading = true;
				bool flag;
				int num;
				int num2;
				bool flag2;
				try
				{
					flag = ZInput.GetKeyDown((KeyCode)27, false) || ZInput.GetButtonDown("JoyButtonB");
					num = ((ZInput.GetKeyDown((KeyCode)274, false) || ZInput.GetButtonDown("JoyDPadDown")) ? 1 : ((ZInput.GetKeyDown((KeyCode)273, false) || ZInput.GetButtonDown("JoyDPadUp")) ? (-1) : 0));
					num2 = ((ZInput.GetKeyDown((KeyCode)275, false) || ZInput.GetButtonDown("JoyDPadRight")) ? 1 : ((ZInput.GetKeyDown((KeyCode)276, false) || ZInput.GetButtonDown("JoyDPadLeft")) ? (-1) : 0));
					flag2 = ZInput.GetKeyDown((KeyCode)13, false) || ZInput.GetButtonDown("JoyButtonA");
				}
				finally
				{
					PickerInput.Reading = false;
				}
				if (flag)
				{
					Close();
					return;
				}
				if (num != 0 && choices.Count > 0)
				{
					selected = (selected + num + choices.Count) % choices.Count;
					if ((float)(selected * 66) < scroll.y)
					{
						scroll.y = selected * 66;
					}
					if ((float)((selected + 1) * 66) > scroll.y + 330f)
					{
						scroll.y = (selected - 4) * 66;
					}
				}
				quantityIndex = (quantityIndex + num2 + quantities.Length) % quantities.Length;
				if (flag2)
				{
					Send(quantities[quantityIndex]);
				}
				return;
			}
		}
		Close();
	}

	private static void Send(int requested)
	{
		//IL_004e: 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_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		if (sending || !Visible || !Object.op_Implicit((Object)(object)station) || !Plugin.Running || choices.Count == 0 || Time.unscaledTime < readyAt)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if (Object.op_Implicit((Object)(object)localPlayer))
		{
			Vector3 val = ((Component)localPlayer).transform.position - ((Component)station).transform.position;
			if (!(((Vector3)(ref val)).sqrMagnitude > 25f))
			{
				if (!PrivateArea.CheckAccess(((Component)station).transform.position, 0f, true, false))
				{
					status = "Access denied.";
					return;
				}
				string name = choices[selected].m_itemData.m_shared.m_name;
				try
				{
					Plugin.Cache.Poll(all: true);
					foreach (Smelter item in capacities.Keys.Where((Smelter x) => !Object.op_Implicit((Object)(object)x)).ToList())
					{
						capacities.Remove(item);
					}
					if (!capacities.TryGetValue(station, out var value))
					{
						value = (capacities[station] = new QueueCapacity());
					}
					int current2 = (int)Queue.Invoke(station, null);
					int num = value.Free(current2, station.m_maxOre, Time.unscaledTime);
					if (num <= 0)
					{
						status = ((value.Pending > 0) ? "Waiting for station update." : "The station is full.");
						return;
					}
					int num2 = Math.Min(Math.Min(requested, num), Processors.Count(((Humanoid)localPlayer).GetInventory(), name, -1, world: true));
					if (num2 <= 0)
					{
						status = "That material is no longer available.";
						RefreshChoices();
						return;
					}
					sending = true;
					Processors.PickerBatch = true;
					status = "";
					for (int num3 = 0; num3 < num2; num3++)
					{
						ItemData val2 = ((Humanoid)localPlayer).GetInventory().GetItem(name, -1, false) ?? Plugin.Cache.First(name, -1);
						if (val2 == null)
						{
							break;
						}
						value.Reserve(Time.unscaledTime);
						if (!(bool)Add.Invoke(station, new object[3] { input, localPlayer, val2 }))
						{
							value.Cancel();
							status = "Station cannot accept this material.";
							break;
						}
						Plugin.Cache.Flush();
						value.Free((int)Queue.Invoke(station, null), station.m_maxOre, Time.unscaledTime);
					}
					RefreshChoices();
					return;
				}
				catch (Exception e)
				{
					status = "Could not add material. Check the log.";
					Plugin.Warn(status, e);
					return;
				}
				finally
				{
					sending = false;
					Processors.PickerBatch = false;
				}
			}
		}
		Close();
	}

	private static void Solid(Rect rect, Color color)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		Color color2 = GUI.color;
		GUI.color = color;
		GUI.DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture);
		GUI.color = color2;
	}

	private static void Styles()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Expected O, but got Unknown
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Expected O, but got Unknown
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Expected O, but got Unknown
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Expected O, but got Unknown
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Expected O, but got Unknown
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Expected O, but got Unknown
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: 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_0118: Expected O, but got Unknown
		if (textStyle == null)
		{
			textStyle = new GUIStyle(GUI.skin.label)
			{
				fontSize = 26,
				alignment = (TextAnchor)3
			};
			textStyle.normal.textColor = Color.white;
			smallStyle = new GUIStyle(textStyle)
			{
				fontSize = 20
			};
			titleStyle = new GUIStyle(textStyle)
			{
				fontSize = 28,
				fontStyle = (FontStyle)1
			};
			titleStyle.normal.textColor = new Color(1f, 0.83f, 0.48f);
			rowStyle = new GUIStyle(textStyle)
			{
				fontStyle = (FontStyle)1,
				wordWrap = true
			};
			countStyle = new GUIStyle(textStyle)
			{
				fontSize = 23,
				alignment = (TextAnchor)5
			};
			emptyStyle = new GUIStyle(titleStyle)
			{
				alignment = (TextAnchor)4
			};
			emptyStyle.normal.textColor = Color.white;
			buttonStyle = new GUIStyle(GUI.skin.button)
			{
				fontSize = 23
			};
		}
	}

	private static void Icon(ItemDrop from, Rect rect)
	{
		//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_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		icons.TryGetValue(from, out var value);
		if (Object.op_Implicit((Object)(object)value) && Object.op_Implicit((Object)(object)value.texture))
		{
			Rect textureRect = value.textureRect;
			float num = ((Texture)value.texture).width;
			float num2 = ((Texture)value.texture).height;
			float num3 = ((Rect)(ref textureRect)).width / ((Rect)(ref textureRect)).height;
			if (num3 > 1f)
			{
				float num4 = ((Rect)(ref rect)).height / num3;
				((Rect)(ref rect)).y = ((Rect)(ref rect)).y + (((Rect)(ref rect)).height - num4) / 2f;
				((Rect)(ref rect)).height = num4;
			}
			else
			{
				float num5 = ((Rect)(ref rect)).width * num3;
				((Rect)(ref rect)).x = ((Rect)(ref rect)).x + (((Rect)(ref rect)).width - num5) / 2f;
				((Rect)(ref rect)).width = num5;
			}
			GUI.DrawTextureWithTexCoords(rect, (Texture)(object)value.texture, new Rect(((Rect)(ref textureRect)).x / num, ((Rect)(ref textureRect)).y / num2, ((Rect)(ref textureRect)).width / num, ((Rect)(ref textureRect)).height / num2), true);
		}
	}

	internal static void Draw()
	{
		//IL_070b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: 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_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Unknown result type (might be due to invalid IL or missing references)
		//IL_025e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0295: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0317: Unknown result type (might be due to invalid IL or missing references)
		//IL_031c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0321: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: 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_0219: Unknown result type (might be due to invalid IL or missing references)
		//IL_034d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0387: Unknown result type (might be due to invalid IL or missing references)
		//IL_036c: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_061a: Unknown result type (might be due to invalid IL or missing references)
		//IL_05eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_0410: Unknown result type (might be due to invalid IL or missing references)
		//IL_044d: Unknown result type (might be due to invalid IL or missing references)
		//IL_06eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0653: Unknown result type (might be due to invalid IL or missing references)
		//IL_06f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_06fd: Invalid comparison between Unknown and I4
		//IL_06a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0511: Unknown result type (might be due to invalid IL or missing references)
		//IL_058e: Unknown result type (might be due to invalid IL or missing references)
		//IL_05a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_04d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_04e4: Unknown result type (might be due to invalid IL or missing references)
		if (!Visible || !Object.op_Implicit((Object)(object)station) || !Object.op_Implicit((Object)(object)Player.m_localPlayer))
		{
			return;
		}
		Styles();
		bool flag = choices.Count == 0;
		float num = Mathf.Clamp((float)Screen.height / 1080f, 0.85f, 2.2f);
		float num2 = Math.Min(5, choices.Count) * 66;
		bool flag2 = !flag && !string.IsNullOrEmpty(status);
		float num3 = (flag ? 460 : ((quantities.Length == 4) ? 720 : 640));
		float num4 = (flag ? 160f : (164f + num2 + (float)(flag2 ? 32 : 0)));
		Matrix4x4 matrix = GUI.matrix;
		try
		{
			GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(num, num, 1f));
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(((float)Screen.width / num - num3) / 2f, ((float)Screen.height / num - num4) / 2f, num3, num4);
			Solid(val, new Color(0.055f, 0.065f, 0.075f, 1f));
			Solid(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width, 3f), new Color(0.8f, 0.6f, 0.25f, 1f));
			GUI.BeginGroup(val);
			try
			{
				if (flag)
				{
					GUI.Label(new Rect(14f, 8f, num3 - 28f, 36f), stationStock, textStyle);
					GUI.Label(new Rect(12f, 54f, num3 - 24f, 48f), syncing ? "Syncing chests…" : (((Object)((Component)station).gameObject).name.StartsWith("charcoal_kiln") ? "No wood available" : "No ores available"), emptyStyle);
					if (GUI.Button(new Rect((num3 - 172f) / 2f, 112f, 172f, 36f), ZInput.IsGamepadActive() ? "Close  [B]" : "Close  [Esc]", buttonStyle))
					{
						Close();
					}
				}
				else
				{
					GUI.Label(new Rect(14f, 8f, 266f, 36f), Localization.instance.Localize(station.m_name), titleStyle);
					GUI.Label(new Rect(288f, 8f, 70f, 36f), "Total", countStyle);
					GUI.Label(new Rect(14f, 48f, num3 - 28f, 36f), stationStock, textStyle);
					int num5 = -1;
					int requested = 1;
					scroll = GUI.BeginScrollView(new Rect(10f, 90f, num3 - 20f, num2), scroll, new Rect(0f, 0f, num3 - 40f, (float)(choices.Count * 66)));
					try
					{
						Rect rect = default(Rect);
						for (int i = 0; i < choices.Count; i++)
						{
							((Rect)(ref rect))..ctor(0f, (float)(i * 66), num3 - 40f, 62f);
							Solid(rect, (i == selected) ? new Color(0.3f, 0.24f, 0.12f, 1f) : new Color(0.105f, 0.12f, 0.135f, 1f));
							if (GUI.Button(new Rect(0f, ((Rect)(ref rect)).y, 350f, ((Rect)(ref rect)).height), GUIContent.none, GUIStyle.none))
							{
								selected = i;
							}
							Icon(choices[i], new Rect(8f, ((Rect)(ref rect)).y + 5f, 52f, 52f));
							GUI.Label(new Rect(70f, ((Rect)(ref rect)).y, 206f, ((Rect)(ref rect)).height), names[choices[i]], rowStyle);
							GUI.Label(new Rect(278f, ((Rect)(ref rect)).y, 70f, ((Rect)(ref rect)).height), counts[i].ToString(), countStyle);
							bool enabled = GUI.enabled;
							GUI.enabled = enabled && Time.unscaledTime >= readyAt;
							for (int j = 0; j < quantities.Length; j++)
							{
								if (i == selected && j == quantityIndex)
								{
									Solid(new Rect((float)(353 + j * 80), ((Rect)(ref rect)).y + 7f, 78f, 48f), new Color(1f, 0.8f, 0.35f));
								}
								if (GUI.Button(new Rect((float)(355 + j * 80), ((Rect)(ref rect)).y + 9f, 74f, 44f), "+" + quantities[j], buttonStyle))
								{
									num5 = i;
									requested = quantities[j];
									quantityIndex = j;
								}
							}
							GUI.enabled = enabled;
							if (i == selected)
							{
								Solid(new Rect(0f, ((Rect)(ref rect)).y, 4f, ((Rect)(ref rect)).height), new Color(1f, 0.8f, 0.35f));
							}
						}
					}
					finally
					{
						GUI.EndScrollView();
					}
					float num6 = 94f + num2;
					if (flag2)
					{
						GUI.Label(new Rect(14f, num6, num3 - 28f, 28f), status, smallStyle);
						num6 += 32f;
					}
					GUI.Label(new Rect(14f, num6, 460f, 28f), ZInput.IsGamepadActive() ? "D-pad ↑↓: material   ←→: amount" : "Arrows ↑↓: material   ←→: amount", smallStyle);
					GUI.Label(new Rect(14f, num6 + 27f, 440f, 28f), (ZInput.IsGamepadActive() ? "A: add " : "Enter: add ") + quantities[quantityIndex], smallStyle);
					bool num7 = GUI.Button(new Rect(num3 - 152f, num6, 140f, 36f), ZInput.IsGamepadActive() ? "Close [B]" : "Close [Esc]", buttonStyle);
					if (num5 >= 0)
					{
						selected = num5;
						Send(requested);
					}
					if (num7)
					{
						Close();
					}
				}
			}
			finally
			{
				GUI.EndGroup();
			}
			if ((int)Event.current.type == 0 || (int)Event.current.type == 4)
			{
				Event.current.Use();
			}
		}
		finally
		{
			GUI.matrix = matrix;
		}
	}
}
internal static class Patches
{
	private class BuilderHover
	{
		private static void Postfix(Container __instance, ref string __result)
		{
			__result = SharingUi.Hover(__instance, ChestBuilder.Hover(__instance, __result));
		}
	}

	private class BuilderCreated
	{
		private static void Postfix(Piece __instance)
		{
			ChestBuilder.Created(__instance);
		}
	}

	private class Register
	{
		private static void Postfix(Container __instance)
		{
			Plugin.Cache.Add(__instance);
		}
	}

	private class Loaded
	{
		private static void Postfix(Container __instance, bool __result)
		{
			if (__result)
			{
				Plugin.Cache.InventoryDirty(__instance.GetInventory());
			}
		}
	}

	private class Busy
	{
		private static void Postfix(Container __instance)
		{
			Plugin.Cache.Loaded(__instance);
		}
	}

	private class SaveGate
	{
		private static bool Prefix(Container __instance)
		{
			return Plugin.ContainerChanged(__instance);
		}
	}

	private class Manual
	{
		private static bool Prefix(Container __instance, Humanoid character, bool hold, bool alt, ref bool __result)
		{
			if (!SharingUi.Interact(__instance, character, hold, alt, ref __result))
			{
				return false;
			}
			Plugin.ManualOpen(__instance);
			return true;
		}
	}

	private class Response
	{
		private static bool Prefix(Container __instance, long uid, bool granted)
		{
			return Plugin.Response(__instance, uid, granted);
		}
	}

	private class ChangeGate
	{
		private static bool Prefix(Inventory __instance)
		{
			return Plugin.InventoryChanging(__instance);
		}

		private static void Postfix(Inventory __instance)
		{
			Plugin.Cache.InventoryDirty(__instance);
		}
	}

	private class Opening
	{
		private static void Prefix()
		{
			Plugin.Opening();
		}

		private static Exception Finalizer(Exception __exception)
		{
			Plugin.Opened();
			return __exception;
		}
	}

	private class Panel
	{
		private static void Prefix()
		{
			Plugin.BeginPanel();
		}

		private static Exception Finalizer(Exception __exception, InventoryGui __instance)
		{
			Plugin.EndPanel(__instance);
			return __exception;
		}
	}

	private class Requirements
	{
		private static void Prefix(InventoryGui __instance)
		{
			Plugin.UpdateSelection(__instance);
		}
	}

	private class Hidden
	{
		private static void Postfix()
		{
			Plugin.ClearManualIntent();
		}
	}

	private class Selection
	{
		private static void Postfix(InventoryGui __instance)
		{
			Plugin.UpdateSelection(__instance);
		}
	}

	private class Display
	{
		private static void Prefix(bool craft, out bool __state)
		{
			__state = ((!craft) ? (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !((Character)Player.m_localPlayer).InPlaceMode()) : (Plugin.DisplayExcluded && (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !((Character)Player.m_localPlayer).InPlaceMode())));
			if (__state)
			{
				Plugin.SuppressCount++;
			}
		}

		private static Exception Finalizer(Exception __exception, bool __state)
		{
			if (__state)
			{
				Plugin.SuppressCount--;
			}
			return __exception;
		}
	}

	private class RecipeScope
	{
		private static void Prefix(Recipe __0, out bool __state)
		{
			__state = !Plugin.RecipeAllowed(__0);
			if (__state)
			{
				Plugin.SuppressCount++;
			}
		}

		private static Exception Finalizer(Exception __exception, bool __state)
		{
			if (__state)
			{
				Plugin.SuppressCount--;
			}
			return __exception;
		}
	}

	private class FirstScope
	{
		private static void Prefix(Recipe __1, out bool __state)
		{
			__state = !Plugin.RecipeAllowed(__1);
			if (__state)
			{
				Plugin.SuppressCount++;
			}
		}

		private static Exception Finalizer(Exception __exception, bool __state)
		{
			if (__state)
			{
				Plugin.SuppressCount--;
			}
			return __exception;
		}
	}

	private class Craft
	{
		[HarmonyPriority(0)]
		private static bool Prefix(out bool __state)
		{
			__state = Plugin.Current == null;
			return __state;
		}

		private static Exception Finalizer(Exception __exception, bool __state)
		{
			return Plugin.Finish(__exception, __state);
		}
	}

	private class HammerMenu
	{
		private static void Prefix()
		{
			Plugin.PrepareHammer();
		}

		private static void Postfix(object __instance)
		{
			BuildWindowRefresh.Opened(__instance);
		}
	}

	private class ToolEquipped
	{
		private static void Postfix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && ((Character)__instance).InPlaceMode())
			{
				Plugin.PrepareHammer();
			}
		}
	}

	private class BuildDisplay
	{
		private static void Prefix()
		{
			Plugin.BeginBuildDisplay();
		}

		private static void Postfix(object __instance, Piece piece)
		{
			HoeStock.Show(__instance, piece);
		}

		private static Exception Finalizer(Exception __exception)
		{
			Plugin.EndBuildDisplay();
			return __exception;
		}
	}

	private class BuildScope
	{
		private static bool Prefix(Player __instance, out bool __state)
		{
			__state = (Object)(object)__instance == (Object)(object)Player.m_localPlayer && Plugin.Current == null;
			if (__state)
			{
				Plugin.PlacementScope++;
				if (Plugin.Running && ((Character)__instance).InPlaceMode())
				{
					Plugin.Cache.Poll(all: false);
				}
			}
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				return Plugin.Current == null;
			}
			return true;
		}

		private static Exception Finalizer(Exception __exception, bool __state)
		{
			if (!__state)
			{
				return __exception;
			}
			return Plugin.FinishPlacement(__exception);
		}
	}

	private static void Hook(Harmony h, MethodBase method, Type callbacks, string pre = null, string post = null, string trans = null, string final = null)
	{
		if (method == null)
		{
			throw new MissingMethodException("Required patch target missing");
		}
		Func<string, HarmonyMethod> func = (string name) => (name != null) ? new HarmonyMethod(AccessTools.Method(callbacks, name, (Type[])null, (Type[])null)) : ((HarmonyMethod)null);
		PatchProcessor val = h.CreateProcessor(method);
		if (pre != null)
		{
			val.AddPrefix(func(pre));
		}
		if (post != null)
		{
			val.AddPostfix(func(post));
		}
		if (trans != null)
		{
			val.AddTranspiler(func(trans));
		}
		if (final != null)
		{
			val.AddFinalizer(func(final));
		}
		val.Patch();
		Patches patchInfo = Harmony.GetPatchInfo(method);
		if (patchInfo == null)
		{
			throw new InvalidOperationException("No Harmony registration for " + method);
		}
		Action<string, IEnumerable<Patch>> obj = delegate(string name, IEnumerable<Patch> list)
		{
			if (name != null && !list.Any((Patch p) => p.owner == "com.modmaking.easycraftfromownchests" && p.PatchMethod == AccessTools.Method(callbacks, name, (Type[])null, (Type[])null)))
			{
				throw new InvalidOperationException("Hook registration missing: " + method?.ToString() + " / " + name);
			}
		};
		obj(pre, patchInfo.Prefixes);
		obj(post, patchInfo.Postfixes);
		obj(trans, patchInfo.Transpilers);
		obj(final, patchInfo.Finalizers);
	}

	internal static void Install(Harmony h)
	{
		Hook(h, AccessTools.Method(typeof(Container), "Awake", (Type[])null, (Type[])null), typeof(Register), null, "Postfix");
		Hook(h, AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null), typeof(Loaded), null, "Postfix");
		Hook(h, AccessTools.Method(typeof(Container), "SetInUse", (Type[])null, (Type[])null), typeof(Busy), null, "Postfix");
		Hook(h, AccessTools.Method(typeof(Container), "OnContainerChanged", (Type[])null, (Type[])null), typeof(SaveGate), "Prefix");
		Hook(h, AccessTools.Method(typeof(Container), "Interact", (Type[])null, (Type[])null), typeof(Manual), "Prefix");
		Hook(h, AccessTools.Method(typeof(Container), "RPC_OpenResponse", (Type[])null, (Type[])null), typeof(Response), "Prefix");
		Hook(h, AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null), typeof(ChangeGate), "Prefix", "Postfix");
		Hook(h, AccessTools.Method(typeof(InventoryGui), "Show", (Type[])null, (Type[])null), typeof(Opening), "Prefix", null, null, "Finalizer");
		Hook(h, AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", (Type[])null, (Type[])null), typeof(Panel), "Prefix", null, null, "Finalizer");
		Hook(h, AccessTools.Method(typeof(InventoryGui), "SetupRequirementList", (Type[])null, (Type[])null), typeof(Requirements), "Prefix");
		Hook(h, AccessTools.Method(typeof(InventoryGui), "Hide", (Type[])null, (Type[])null), typeof(Hidden), null, "Postfix");
		Hook(h, AccessTools.Method(typeof(InventoryGui), "SetRecipe", (Type[])null, (Type[])null), typeof(Selection), null, "Postfix");
		Hook(h, AccessTools.Method(typeof(InventoryGui), "SetupRequirement", (Type[])null, (Type[])null), typeof(Display), "Prefix", null, null, "Finalizer");
		Hook(h, AccessTools.Method(typeof(Player), "HaveRequirementItems", (Type[])null, (Type[])null), typeof(RecipeScope), "Prefix", null, null, "Finalizer");
		Hook(h, AccessTools.Method(typeof(Player), "GetFirstRequiredItem", (Type[])null, (Type[])null), typeof(FirstScope), "Prefix", null, null, "Finalizer");
		Hook(h, AccessTools.Method(typeof(InventoryGui), "DoCrafting", (Type[])null, (Type[])null), typeof(Craft), "Prefix", null, null, "Finalizer");
		Hook(h, AccessTools.Method(AccessTools.TypeByName("BuildUi"), "OpenBuildMenu", (Type[])null, (Type[])null), typeof(HammerMenu), "Prefix", "Postfix");
		Hook(h, AccessTools.Method(AccessTools.TypeByName("BuildUi"), "Update", (Type[])null, (Type[])null), typeof(BuildWindowRefresh), null, "Tick");
		Hook(h, AccessTools.Method(AccessTools.TypeByName("Hud"), "SetupPieceInfo", (Type[])null, (Type[])null), typeof(BuildDisplay), "Prefix", "Postfix", null, "Finalizer");
		Hook(h, AccessTools.Method(typeof(Player), "SetPlaceMode", (Type[])null, (Type[])null), typeof(ToolEquipped), null, "Postfix");
		Hook(h, AccessTools.Method(typeof(Player), "UpdatePlacement", (Type[])null, (Type[])null), typeof(BuildScope), "Prefix", null, null, "Finalizer");
		Hook(h, AccessTools.Method(typeof(Container), "GetHoverText", (Type[])null, (Type[])null), typeof(BuilderHover), null, "Postfix");
		Hook(h, AccessTools.Method(typeof(Piece), "SetCreator", (Type[])null, (Type[])null), typeof(BuilderCreated), null, "Postfix");
		string[] array = new string[5] { "Smelter:OnHoverAddOre,OnHoverAddFuel", "CookingStation:GetHoverText,OnHoverFuelSwitch", "Fermenter:GetHoverText", "Fireplace:GetHoverText", "ShieldGenerator:OnHoverAddFuel" };
		for (int i = 0; i < array.Length; i++)
		{
			string[] array2 = array[i].Split(':');
			string[] array3 = array2[1].Split(',');
			foreach (string text in array3)
			{
				Hook(h, AccessTools.Method(AccessTools.TypeByName(array2[0]), text, (Type[])null, (Type[])null), typeof(StationStock), null, "Hover");
			}
		}
		foreach (MethodBase item in Processors.Targets())
		{
			Hook(h, item, typeof(Processors), null, null, "Transpiler", "Finalizer");
		}
		array = new string[3] { "Smelter", "CookingStation", "Fermenter" };
		foreach (string text2 in array)
		{
			Hook(h, AccessTools.Method(AccessTools.TypeByName(text2), "FindCookableItem", (Type[])null, (Type[])null), typeof(Processors), null, "Find");
		}
		Hook(h, AccessTools.Method(typeof(Smelter), "OnAddOre", (Type[])null, (Type[])null), typeof(OrePicker), "Open");
		Hook(h, AccessTools.Method(typeof(GameCamera), "UpdateMouseCapture", (Type[])null, (Type[])null), typeof(OrePicker), null, "ReleaseCursor");
		Hook(h, AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null), typeof(OrePicker), "BlockInput");
		array = new string[6] { "GetButton", "GetButtonDown", "GetButtonUp", "GetMouseButton", "GetMouseButtonDown", "GetMouseButtonUp" };
		foreach (string text3 in array)
		{
			Hook(h, AccessTools.Method(typeof(ZInput), text3, (Type[])null, (Type[])null), typeof(PickerInput), "BlockGameButton");
		}
		array = new string[7] { "GetJoyLeftStickX", "GetJoyLeftStickY", "GetJoyRightStickX", "GetJoyRightStickY", "GetJoyLTrigger", "GetJoyRTrigger", "GetMouseScrollWheel" };
		foreach (string text4 in array)
		{
			Hook(h, AccessTools.Method(typeof(ZInput), text4, (Type[])null, (Type[])null), typeof(PickerInput), "BlockGameAxis");
		}
		array = new string[3] { "GetJoyLeftStick", "GetJoyRightStick", "GetMouseDelta" };
		foreach (string text5 in array)
		{
			Hook(h, AccessTools.Method(typeof(ZInput), text5, (Type[])null, (Type[])null), typeof(PickerInput), "BlockGameVector");
		}
		Hook(h, AccessTools.Method(AccessTools.TypeByName("Menu"), "Update", (Type[])null, (Type[])null), typeof(PickerInput), "BlockMenus");
		foreach (MethodBase item2 in Targets())
		{
			Hook(h, item2, typeof(Patches), null, null, "Transpiler");
		}
	}

	internal static IEnumerable<MethodBase> Targets()
	{
		yield return AccessTools.Method(typeof(Player), "HaveRequirementItems", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(Player), "GetFirstRequiredItem", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(InventoryGui), "SetupRequirement", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(Player), "ConsumeResources", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(InventoryGui), "DoCrafting", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(Player), "HaveRequirements", new Type[2]
		{
			typeof(Piece),
			typeof(RequirementMode)
		}, (Type[])null);
		yield return AccessTools.Method(typeof(Player), "TryPlacePiece", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(Player), "PlacePiece", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(Player), "UpdatePlacement", (Type[])null, (Type[])null);
	}

	internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod, ILGenerator generator)
	{
		List<CodeInstruction> list = instructions.ToList();
		int replacements = 0;
		string target = __originalMethod.Name;
		if (target == "DoCrafting")
		{
			Label begin = generator.DefineLabel();
			list[0].labels.Add(begin);
			yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
			yield return new CodeInstruction(OpCodes.Ldarg_1, (object)null);
			yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Plugin), "BeginNative", (Type[])null, (Type[])null));
			yield return new CodeInstruction(OpCodes.Brtrue, (object)begin);
			yield return new CodeInstruction(OpCodes.Ret, (object)null);
		}
		foreach (CodeInstruction item in list)
		{
			MethodInfo methodInfo = item.operand as MethodInfo;
			string text = null;
			if (methodInfo != null && methodInfo.DeclaringType == typeof(Inventory))
			{
				if (methodInfo.Name == "CountItems" && methodInfo.GetParameters().Length == 3)
				{
					text = ((target == "HaveRequirements") ? "BuildCount" : "Count");
				}
				if (methodInfo.Name == "HaveItem" && target == "HaveRequirements" && methodInfo.GetParameters().Length == 2)
				{
					text = "BuildHave";
				}
				if (methodInfo.Name == "GetItem" && target == "GetFirstRequiredItem" && methodInfo.GetParameters().Length == 3)
				{
					text = "GetItem";
				}
				if (methodInfo.Name == "RemoveItem" && methodInfo.GetParameters().Length == 4 && methodInfo.GetParameters()[0].ParameterType == typeof(string))
				{
					text = "Remove";
				}
				if (methodInfo.Name == "ItemCheated" && methodInfo.GetParameters().Length == 3 && methodInfo.GetParameters()[0].ParameterType == typeof(Requirement[]))
				{
					text = "ItemCheated";
				}
				if (methodInfo.Name == "AddItem" && target == "DoCrafting" && methodInfo.GetParameters().Length == 10)
				{
					text = "AddCrafted";
				}
			}
			if (methodInfo != null && target == "DoCrafting" && methodInfo.DeclaringType == typeof(InventoryGui) && methodInfo.Name == "UpdateCraftingPanel")
			{
				text = "CommitAndRefresh";
			}
			if (methodInfo != null && target == "TryPlacePiece" && methodInfo.DeclaringType == typeof(Player) && methodInfo.Name == "PlacePiece")
			{
				text = "PlaceWithResources";
			}
			if (methodInfo != null && target == "UpdatePlacement" && methodInfo.DeclaringType == typeof(Player) && methodInfo.Name == "ConsumeResources")
			{
				text = "ConsumeBuilding";
			}
			if (methodInfo != null && target == "PlacePiece" && methodInfo.DeclaringType == typeof(Object) && methodInfo.Name == "Instantiate" && methodInfo.IsGenericMethod && methodInfo.GetGenericArguments()[0] == typeof(GameObject) && methodInfo.GetParameters().Length == 3)
			{
				text = "InstantiateBuilding";
			}
			if (text != null)
			{
				item.opcode = OpCodes.Call;
				item.operand = AccessTools.Method(typeof(Plugin), text, (Type[])null, (Type[])null);
				int num = replacements + 1;
				replacements = num;
			}
			yield return item;
		}
		int num2;
		switch (target)
		{
		default:
			num2 = 1;
			break;
		case "GetFirstRequiredItem":
		case "HaveRequirements":
		case "TryPlacePiece":
			num2 = 2;
			break;
		case "DoCrafting":
			num2 = 7;
			break;
		}
		int num3 = num2;
		if (replacements != num3)
		{
			throw new InvalidOperationException("Unsupported " + target + " layout: expected " + num3 + ", found " + replacements);
		}
	}
}
internal static class PickerInput
{
	internal static bool Open;

	internal static bool SharingOpen;

	internal static bool Reading;

	internal static bool ExternalText;

	internal static float BlockUntil;

	internal static bool Capturing
	{
		get
		{
			if (!Open && !SharingOpen)
			{
				return Time.unscaledTime < BlockUntil;
			}
			return true;
		}
	}

	internal static bool BlockPlayerInput(ref bool __result)
	{
		if (!Capturing)
		{
			return true;
		}
		__result = false;
		return false;
	}

	internal static bool BlockGameButton(ref bool __result)
	{
		if (!Capturing || Reading || ExternalText)
		{
			return true;
		}
		__result = false;
		return false;
	}

	internal static bool BlockGameAxis(ref float __result)
	{
		if (!Capturing || Reading || ExternalText)
		{
			return true;
		}
		__result = 0f;
		return false;
	}

	internal static bool BlockGameVector(ref Vector2 __result)
	{
		//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)
		if (!Capturing || Reading || ExternalText)
		{
			return true;
		}
		__result = Vector2.zero;
		return false;
	}

	internal static bool BlockMenus()
	{
		return !Capturing;
	}
}
internal static class Processors
{
	internal static bool PickerBatch;

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

	internal static IEnumerable<ItemDrop> Inputs(object station)
	{
		FieldInfo fieldInfo = AccessTools.Field(station.GetType(), "m_conversion");
		IEnumerable enumerable = ((fieldInfo == null) ? null : (fieldInfo.GetValue(station) as IEnumerable));
		if (enumerable == null)
		{
			yield break;
		}
		foreach (object item in enumerable)
		{
			object? value = AccessTools.Field(item.GetType(), "m_from").GetValue(item);
			ItemDrop val = (ItemDrop)((value is ItemDrop) ? value : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				yield return val;
			}
		}
	}

	internal static void Find(object __instance, Inventory inventory, ref ItemData __result)
	{
		if (__result != null || !Local(inventory))
		{
			return;
		}
		foreach (ItemDrop item in Inputs(__instance))
		{
			ItemData val = Plugin.Cache.First(item.m_itemData.m_shared.m_name, -1);
			if (val != null)
			{
				__result = val;
				break;
			}
		}
	}

	public static int Count(Inventory inv, string name, int quality, bool world)
	{
		return checked(inv.CountItems(name, quality, world) + (Local(inv) ? Plugin.Cache.Count(name, quality, world) : 0));
	}

	public static bool Have(Inventory inv, string name, bool world)
	{
		return Count(inv, name, -1, world) > 0;
	}

	public static int CountNames(Inventory inv, string[] names, int quality, bool world, bool stacks)
	{
		int num = inv.CountItemsByName(names, quality, world, stacks);
		if (!Local(inv) || stacks || names == null)
		{
			return num;
		}
		foreach (string item in names.Distinct())
		{
			num = checked(num + Plugin.Cache.Count(item, quality, world));
		}
		return num;
	}

	public static bool RemoveOne(Inventory inv, ItemData item)
	{
		return RemoveExact(inv, item, 1);
	}

	public static bool RemoveExact(Inventory inv, ItemData item, int amount)
	{
		if (!Local(inv) || inv.GetAllItems().Contains(item))
		{
			return inv.RemoveItem(item, amount);
		}
		if (item == null || amount <= 0)
		{
			throw new InvalidOperationException("Invalid processor debit");
		}
		Debit(inv, item.m_shared.m_name, amount, item.m_quality, world: true, item);
		return true;
	}

	public static void RemoveNamed(Inventory inv, string name, int amount, int quality, bool world)
	{
		if (!Local(inv) || inv.CountItems(name, quality, world) >= amount)
		{
			inv.RemoveItem(name, amount, quality, world);
		}
		else
		{
			Debit(inv, name, amount, quality, world, null);
		}
	}

	internal static void Debit(Inventory inv, string name, int amount, int quality, bool world, ItemData exact)
	{
		if (Plugin.Current != null)
		{
			throw new InvalidOperationException("Another inventory transaction is active");
		}
		if (!PickerBatch)
		{
			Plugin.Cache.Poll(all: true);
		}
		else
		{
			Plugin.Cache.Flush();
		}
		ResourceKey resourceKey = new ResourceKey(name, quality, world);
		CraftTransaction craftTransaction = new CraftTransaction(Player.m_localPlayer, null);
		try
		{
			if (exact == null)
			{
				craftTransaction.Plan(resourceKey, amount);
			}
			else
			{
				Chest chest = Plugin.Cache.Chests.FirstOrDefault((Chest x) => x.Ready && x.Inventory.GetAllItems().Contains(exact));
				if (chest == null || !Plugin.Cache.Usable(chest) || !chest.View.IsOwner() || !Plugin.AccessReady(chest) || exact.m_stack < amount || (world && exact.m_worldLevel < Game.m_worldLevel))
				{
					throw new InvalidOperationException("Selected processor material is no longer available");
				}
				craftTransaction.Sources.Add(chest);
				craftTransaction.Remaining[resourceKey] = amount;
				craftTransaction.Debits.Add(new CraftTransaction.Debit
				{
					Inventory = chest.Inventory,
					Item = exact,
					Amount = amount,
					Cost = resourceKey
				});
			}
			Plugin.Current = craftTransaction;
			craftTransaction.Acquire();
			craftTransaction.Remove(inv, name, amount, quality, world);
			craftTransaction.Commit();
		}
		catch (Exception ex)
		{
			Plugin.Finish(ex, ownsScope: true);
			throw new InvalidOperationException("Processor material debit failed; no station request was sent", ex);
		}
		finally
		{
			if (Plugin.Current == craftTransaction)
			{
				Plugin.Finish(null, ownsScope: true);
			}
		}
	}

	internal static IEnumerable<MethodBase> Targets()
	{
		string[] array = new string[5] { "Smelter:OnAddOre,OnAddFuel,CanUseItems", "CookingStation:CookItem,OnAddFuelSwitch,CanUseItems", "Fermenter:AddItem", "Fireplace:Interact,UseItem,CanUseItems", "ShieldGenerator:OnAddFuel" };
		for (int i = 0; i < array.Length; i++)
		{
			string[] array2 = array[i].Split(':');
			Type type = AccessTools.TypeByName(array2[0]);
			if (type == null)
			{
				throw new MissingMemberException(array2[0]);
			}
			string[] array3 = array2[1].Split(',');
			foreach (string text in array3)
			{
				yield return AccessTools.Method(type, text, (Type[])null, (Type[])null);
			}
		}
	}

	internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
	{
		int replaced = 0;
		foreach (CodeInstruction instruction in instructions)
		{
			MethodInfo methodInfo = instruction.operand as MethodInfo;
			string text = null;
			if (methodInfo != null && methodInfo.DeclaringType == typeof(Inventory))
			{
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (methodInfo.Name == "HaveItem" && parameters.Length == 2 && parameters[0].ParameterType == typeof(string))
				{
					text = "Have";
				}
				if (methodInfo.Name == "CountItems" && parameters.Length == 3)
				{
					text = "Count";
				}
				if (methodInfo.Name == "CountItemsByName" && parameters.Length == 4)
				{
					text = "CountNames";
				}
				if (methodInfo.Name == "RemoveOneItem" && parameters.Length == 1)
				{
					text = "RemoveOne";
				}
				if (methodInfo.Name == "RemoveItem" && parameters.Length == 2 && parameters[0].ParameterType == typeof(ItemData))
				{
					text = "RemoveExact";
				}
				if (methodInfo.Name == "RemoveItem" && parameters.Length == 4 && parameters[0].ParameterType == typeof(string))
				{
					text = "RemoveNamed";
				}
			}
			if (text != null)
			{
				instruction.opcode = OpCodes.Call;
				instruction.operand = AccessTools.Method(typeof(Processors), text, (Type[])null, (Type[])null);
				int num = replaced + 1;
				replaced = num;
			}
			yield return instruction;
		}
		string text2 = __originalMethod.DeclaringType.Name + "." + __originalMethod.Name;
		int num2;
		switch (text2)
		{
		default:
			num2 = 1;
			break;
		case "Smelter.OnAddFuel":
		case "Smelter.CanUseItems":
		case "CookingStation.OnAddFuelSwitch":
		case "Fireplace.Interact":
		case "ShieldGenerator.OnAddFuel":
			num2 = 2;
			break;
		case "Fireplace.UseItem":
			num2 = 3;
			break;
		}
		int num3 = num2;
		if (replaced != num3)
		{
			throw new InvalidOperationException("Unsupported processor layout: " + text2 + "; expected " + num3 + ", got " + replaced);
		}
	}

	internal static Exception Finalizer(Exception __exception)
	{
		if (__exception != null)
		{
			Plugin.Warn("Station interaction failed. Check material and station state before retrying; no automatic post-dispatch refund.", __exception);
		}
		return __exception;
	}
}
internal sealed class QueueCapacity
{
	internal const float ReservationSeconds = 3f;

	private readonly List<float> sent = new List<float>();

	private int observed;

	internal int Pending => sent.Count;

	internal int Free(int current, int maximum, float now = 0f)
	{
		int num = Math.Min(sent.Count, Math.Max(0, current - observed));
		if (num > 0)
		{
			sent.RemoveRange(0, num);
		}
		observed = current;
		sent.RemoveAll((float time) => now - time >= 3f || now < time);
		return Math.Max(0, maximum - current - Pending);
	}

	internal void Reserve(float now = 0f)
	{
		sent.Add(now);
	}

	internal void Cancel()
	{
		if (sent.Count > 0)
		{
			sent.RemoveAt(sent.Count - 1);
		}
	}
}
internal static class SharingBatch
{
	private sealed class Target
	{
		internal Chest chest;

		internal int revision;
	}

	internal static readonly int[] Radii = new int[5] { 1, 5, 10, 20, 50 };

	private static readonly List<Target> targets = new List<Target>();

	private static string[] clipboard;

	private static string[] members;

	private static long clipboardPlayer;

	private static Chest anchor;

	private static Vector3 center;

	private static int radius;

	private static int index;

	private static int saved;

	private static int skipped;

	private static float deadline;

	internal static string Status = "";

	internal static bool Running { get; private set; }

	internal static int Count => targets.Count;

	internal static string[] RecipientKeys
	{
		get
		{
			if (members != null)
			{
				return members.ToArray();
			}
			return new string[0];
		}
	}

	internal static int MemberCount
	{
		get
		{
			if (members != null)
			{
				return members.Length;
			}
			return 0;
		}
	}

	internal static int CopiedCount
	{
		get
		{
			if (!HasCopy)
			{
				return 0;
			}
			return clipboard.Length;
		}
	}

	internal static bool HasCopy
	{
		get
		{
			if (clipboard != null && Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return clipboardPlayer == Player.m_localPlayer.GetPlayerID();
			}
			return false;
		}
	}

	internal static void Clear()
	{
		Cancel();
		clipboard = null;
		clipboardPlayer = 0L;
		Status = "";
	}

	internal static void Cancel()
	{
		Running = false;
		targets.Clear();
		anchor = null;
		members = null;
	}

	internal static void Copy(string id)
	{
		if (Running)
		{
			throw new InvalidOperationException("Wait for the current operation to finish.");
		}
		Chest c = SharingService.Find(id);
		SharingService.Validate(c);
		clipboard = SharingService.Current(c).members.ToArray();
		clipboardPlayer = Player.m_localPlayer.GetPlayerID();
		Status = "Copied " + clipboard.Length + " shared player(s). Open another chest and choose Paste.";
	}

	internal static void Preview(string id, int distance)
	{
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		if (Running)
		{
			throw new InvalidOperationException("Wait for the current operation to finish.");
		}
		Cancel();
		anchor = SharingService.Find(id);
		SharingService.Validate(anchor);
		if (distance != 0 && !Radii.Contains(distance))
		{
			throw new InvalidOperationException("Unsupported radius.");
		}
		if (distance == 0 && !HasCopy)
		{
			throw new InvalidOperationException("Copy a sharing list first.");
		}
		radius = distance;
		center = ((Component)anchor.Container).transform.position;
		members = ((distance == 0) ? clipboard : SharingService.Current(anchor).members).ToArray();
		foreach (Chest chest in Plugin.Cache.Chests)
		{
			if (Eligible(chest))
			{
				try
				{
					targets.Add(new Target
					{
						chest = chest,
						revision = SharingService.Current(chest).revision
					});
				}
				catch
				{
				}
			}
		}
		Status = "Replace sharing on " + Count + " chest(s) with " + MemberCount + " shared player(s)?";
	}

	private static bool Eligible(Chest c)
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		if (c == null || !Plugin.Cache.Usable(c) || c.Piece.GetCreator() != Player.m_localPlayer.GetPlayerID())
		{
			return false;
		}
		if (radius != 0)
		{
			if (c != anchor)
			{
				Vector3 val = ((Component)c.Container).transform.position - center;
				return ((Vector3)(ref val)).sqrMagnitude <= (float)(radius * radius);
			}
			return false;
		}
		return c == anchor;
	}

	internal static void Start()
	{
		if (!Running && targets.Count != 0)
		{
			SharingService.Validate(anchor);
			index = (saved = (skipped = 0));
			deadline = Time.unscaledTime + 9f;
			Running = true;
			Status = "Applying sharing…";
		}
	}