Decompiled source of CraftFromDrawersAndChests v1.0.4

plugins\CraftFromDrawersAndChests.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CraftFromDrawersAndChests.Logic;
using CraftFromDrawersAndChests.Pull;
using CraftFromDrawersAndChests.Storage;
using HarmonyLib;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using MultiUserChest;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace CraftFromDrawersAndChests
{
	public static class ModInfo
	{
		public const string ModName = "CraftFromDrawersAndChests";

		public const string ModGuid = "NorCal_Nerds.CraftFromDrawersAndChests";

		public const string Version = "1.0.4";

		public const string ThunderstoreTeam = "NorCal_Nerds";

		public const string ThunderstorePackage = "CraftFromDrawersAndChests";

		public const string GitHubRepo = "ghstwhl/CraftFromDrawersAndChests";

		public const string ConsoleCommand = "craftfromdrawersandchests";

		public const string ConsoleCommandAlias = "cfdac";
	}
	[BepInPlugin("NorCal_Nerds.CraftFromDrawersAndChests", "CraftFromDrawersAndChests", "1.0.4")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInIncompatibility("aedenthorn.CraftFromContainers")]
	[BepInIncompatibility("Azumatt.AzuCraftyBoxes")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	[SynchronizationMode(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public static Plugin Instance;

		public static Harmony Harmony;

		internal static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger;

		private void Awake()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			Instance = this;
			Harmony = new Harmony("NorCal_Nerds.CraftFromDrawersAndChests");
			try
			{
				PluginConfig.Init(((BaseUnityPlugin)this).Config);
				PluginConfig.ReadInto(PluginConfig.Flags);
			}
			catch (Exception ex)
			{
				if (PluginConfig.Flags != null)
				{
					PluginConfig.Flags.ModEnabled = false;
				}
				Log.LogError((object)("CraftFromDrawersAndChests: configuration initialisation failed; the mod is being left fully disabled until this is fixed (Harmony patches are still applied, but every config-gated feature is inert). Cause: " + ex));
			}
			Harmony.PatchAll(typeof(Plugin).Assembly);
			LogPatchReport();
		}

		private static void LogPatchReport()
		{
			List<string> list = new List<string>();
			foreach (MethodBase patchedMethod in Harmony.GetPatchedMethods())
			{
				list.Add(patchedMethod.DeclaringType.Name + "." + patchedMethod.Name);
			}
			list.Sort();
			int count = list.Count;
			Log.LogInfo((object)("CraftFromDrawersAndChests 1.0.4: Harmony patched " + count + " game method(s): " + string.Join(", ", list.ToArray()) + " (Harmony's no-argument PatchAll() would have inferred '" + InferPatchAllAssembly() + "'.)"));
			if (count == 0)
			{
				Log.LogError((object)"CraftFromDrawersAndChests: Harmony applied ZERO patches, so every feature is inert (no chest pulling, no drawer pulling, no per-storage switch, no console command). Nothing else in this log marks that state.");
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static string InferPatchAllAssembly()
		{
			try
			{
				StackFrame frame = new StackTrace().GetFrame(1);
				if (frame == null)
				{
					return "<null frame>";
				}
				MethodBase method = frame.GetMethod();
				if (method == null)
				{
					return "<null method>";
				}
				Type reflectedType = method.ReflectedType;
				if (reflectedType == null)
				{
					return "<null reflected type>";
				}
				return reflectedType.Assembly.GetName().Name;
			}
			catch (Exception ex)
			{
				return "<threw " + ex.GetType().Name + ">";
			}
		}
	}
	public static class PluginConfig
	{
		public static ConfigFile File;

		public static FeatureFlags Flags;

		private static ConfigEntry<bool> modEnabled;

		private static ConfigEntry<bool> isDebug;

		private static ConfigEntry<float> maxRange;

		private static ConfigEntry<string> pullItemsKey;

		private static ConfigEntry<string> toggleStorageKey;

		private static ConfigEntry<string> preventModKey;

		private static ConfigEntry<string> fillAllModKey;

		private static ConfigEntry<bool> switchPrevent;

		private static ConfigEntry<bool> enableChests;

		private static ConfigEntry<bool> enableDrawers;

		private static ConfigEntry<bool> leaveOne;

		private static ConfigEntry<bool> pullByDistance;

		private static ConfigEntry<bool> pullFuel;

		private static ConfigEntry<bool> pullOre;

		private static ConfigEntry<bool> pullCooking;

		private static ConfigEntry<string> fuelDisallowTypes;

		private static ConfigEntry<string> oreDisallowTypes;

		private static ConfigEntry<string> resourceString;

		private static ConfigEntry<string> pulledMessage;

		private static ConfigEntry<Color> flashColor;

		private static ConfigEntry<Color> unFlashColor;

		private static ConfigEntry<bool> ignoreShipContainers;

		private static ConfigEntry<bool> ignoreWagonContainers;

		private static ConfigEntry<bool> ignoreWoodChests;

		private static ConfigEntry<bool> ignorePrivateChests;

		private static ConfigEntry<bool> ignoreBlackMetalChests;

		private static ConfigEntry<bool> ignoreReinforcedChests;

		public static Color FlashColor => flashColor.Value;

		public static Color UnFlashColor => unFlashColor.Value;

		public static void Init(ConfigFile config)
		{
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			File = config;
			Flags = new FeatureFlags();
			modEnabled = Bind(config, "1 - Server", "Enable mod", defaultValue: true, "Enable this mod.");
			maxRange = Bind(config, "1 - Server", "Maximum range", 10f, "The maximum distance at which storage is used. Measured from the player, or from the station for the fuel, ore and cooking features. 0 or less means unlimited, matching CraftFromContainers.");
			enableChests = Bind(config, "1 - Server", "Enable chests", defaultValue: true, "Pull from vanilla chests.");
			enableDrawers = Bind(config, "1 - Server", "Enable drawers", defaultValue: true, "Pull from KG Item Drawers drawers.");
			leaveOne = Bind(config, "1 - Server", "Leave one", defaultValue: false, "Always leave the last item of each type in the source.");
			pullByDistance = Bind(config, "1 - Server", "Pull by distance", defaultValue: false, "Pull from the nearest storage first.");
			pullFuel = Bind(config, "1 - Server", "Pull fuel", defaultValue: true, "Let the mod feed fuel to fireplaces and smelters.");
			pullOre = Bind(config, "1 - Server", "Pull ore", defaultValue: true, "Let the mod feed ore to smelters.");
			pullCooking = Bind(config, "1 - Server", "Pull cooking", defaultValue: true, "Let the mod feed cooking ingredients to cooking stations.");
			fuelDisallowTypes = Bind(config, "1 - Server", "Fuel disallow types", "RoundLog,FineWood", "Item prefab names that may not be used as fuel, comma-separated.");
			oreDisallowTypes = Bind(config, "1 - Server", "Ore disallow types", "RoundLog,FineWood", "Item prefab names that may not be used as ore, comma-separated.");
			resourceString = Bind(config, "1 - Server", "Resource string", "{0}/{1}", "Format used for the required/available resource readout; {0} is available and {1} is required. Empty leaves it at the default.");
			pulledMessage = Bind(config, "1 - Server", "Pulled message", "Pulled items to inventory", "HUD message shown after pulling items into the inventory. Empty disables it.");
			flashColor = Bind<Color>(config, "1 - Server", "Flash color", Color.yellow, "Colour the resource readout flashes when the materials come from storage.");
			unFlashColor = Bind<Color>(config, "1 - Server", "Unflash color", Color.white, "Colour the resource readout alternates with while flashing. Set both colours equal to disable the flash.");
			ignoreShipContainers = Bind(config, "1 - Server", "Ignore ship containers", defaultValue: false, "Ignore chests aboard ships.");
			ignoreWagonContainers = Bind(config, "1 - Server", "Ignore wagon containers", defaultValue: false, "Ignore chests aboard carts.");
			ignoreWoodChests = Bind(config, "1 - Server", "Ignore wood chests", defaultValue: false, "Ignore wood chests.");
			ignorePrivateChests = Bind(config, "1 - Server", "Ignore private chests", defaultValue: false, "Ignore private chests.");
			ignoreBlackMetalChests = Bind(config, "1 - Server", "Ignore black metal chests", defaultValue: false, "Ignore black metal chests.");
			ignoreReinforcedChests = Bind(config, "1 - Server", "Ignore reinforced chests", defaultValue: false, "Ignore reinforced chests.");
			pullItemsKey = Bind(config, "2 - Keys", "Pull items key", "left ctrl", "Held key that pulls a recipe's ingredients into the inventory. Use a Unity key name.");
			toggleStorageKey = Bind(config, "2 - Keys", "Toggle storage key", "x", "Pressed to toggle a hovered storage on or off. The crosshair decides what it applies to, so a single key is usual and a movement key must never be used: it would toggle whatever you look at while walking. A combination such as \"left shift + e\" also works - every name but the last is held, and the last is pressed. Empty leaves the toggle unbound.");
			preventModKey = Bind(config, "2 - Keys", "Prevent mod key", "left alt", "Held key that suppresses this mod's pulling. Use a Unity key name.");
			fillAllModKey = Bind(config, "2 - Keys", "Fill all mod key", "left shift", "Held key that asks storage for a station's whole remaining capacity instead of a single item, taking as much of it as the storage can supply. Use a Unity key name.");
			switchPrevent = Bind(config, "2 - Keys", "Switch prevent", defaultValue: false, "If true, holding the prevent key enables this mod's pulling instead of suppressing it.");
			isDebug = Bind(config, "9 - Debug", "Debug", defaultValue: false, "Print debug messages to the log.");
			SynchronizationManager.OnConfigurationSynchronized += RefreshFlags;
		}

		public static void ReadInto(FeatureFlags flags)
		{
			flags.ModEnabled = modEnabled.Value;
			flags.IsDebug = isDebug.Value;
			flags.MaxRange = maxRange.Value;
			flags.PullItemsKey = pullItemsKey.Value;
			flags.ToggleStorageKey = toggleStorageKey.Value;
			flags.PreventModKey = preventModKey.Value;
			flags.FillAllModKey = fillAllModKey.Value;
			flags.SwitchPrevent = switchPrevent.Value;
			flags.EnableChests = enableChests.Value;
			flags.EnableDrawers = enableDrawers.Value;
			flags.LeaveOne = leaveOne.Value;
			flags.PullByDistance = pullByDistance.Value;
			flags.PullFuel = pullFuel.Value;
			flags.PullOre = pullOre.Value;
			flags.PullCooking = pullCooking.Value;
			flags.FuelDisallowTypes = fuelDisallowTypes.Value;
			flags.OreDisallowTypes = oreDisallowTypes.Value;
			flags.ResourceString = resourceString.Value;
			flags.PulledMessage = pulledMessage.Value;
			flags.IgnoreShipContainers = ignoreShipContainers.Value;
			flags.IgnoreWagonContainers = ignoreWagonContainers.Value;
			flags.IgnoreWoodChests = ignoreWoodChests.Value;
			flags.IgnorePrivateChests = ignorePrivateChests.Value;
			flags.IgnoreBlackMetalChests = ignoreBlackMetalChests.Value;
			flags.IgnoreReinforcedChests = ignoreReinforcedChests.Value;
		}

		private static void RefreshFlags(object sender, ConfigurationSynchronizationEventArgs e)
		{
			ReadInto(Flags);
		}

		private static ConfigEntry<T> Bind<T>(ConfigFile config, string section, string key, T defaultValue, string description)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			ConfigEntry<T> obj = config.Bind<T>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			obj.SettingChanged += OnSettingChanged;
			return obj;
		}

		private static void OnSettingChanged(object sender, EventArgs e)
		{
			FeatureFlags flags = Flags;
			if (flags != null)
			{
				ReadInto(flags);
			}
		}
	}
	public static class MultiUserChestBridge
	{
		private static readonly ManualLogSource Log = Logger.CreateLogSource("CraftFromDrawersAndChests");

		public static int RemoveFromChest(Container chest, ItemData item, Inventory destination, int amount)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)chest == (Object)null || item == null || item.m_shared == null || destination == null || amount <= 0)
			{
				return 0;
			}
			ZNetView nview = chest.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return 0;
			}
			if (!nview.HasOwner())
			{
				nview.ClaimOwnership();
			}
			string name = item.m_shared.m_name;
			int num = CountMatching(destination, name);
			Vector2i val = default(Vector2i);
			((Vector2i)(ref val))..ctor(-1, -1);
			ZDOID val2 = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Character)Player.m_localPlayer).GetZDOID() : ZDOID.None);
			RequestChestRemove val3 = ContainerHandler.RemoveItemFromChest(chest, item, destination, val, val2, amount, (ItemData)null);
			int delivered = CountMatching(destination, name) - num;
			bool flag = ChestTake.WasRouted(val3 == null, val3?.dragAmount ?? 0);
			if (flag)
			{
				ManualLogSource log = Log;
				string[] obj = new string[7] { "Chest is managed by another player; routed a request for ", null, null, null, null, null, null };
				int dragAmount = val3.dragAmount;
				obj[1] = dragAmount.ToString();
				obj[2] = "x '";
				obj[3] = PrefabName(item);
				obj[4] = "'. The owner applies it asynchronously, so the moved count cannot be reported here yet; returning ";
				obj[5] = (-1).ToString();
				obj[6] = " (deferred).";
				log.LogWarning((object)string.Concat(obj));
			}
			else if (ChestTake.DeliveredNothing(flag, delivered))
			{
				Log.LogWarning((object)("MultiUserChest delivered nothing into the destination for '" + PrefabName(item) + "' (the chest may be empty or the destination full)."));
			}
			return ChestTake.Result(flag, delivered);
		}

		public static bool CanRemoveSynchronously(Container chest)
		{
			ZNetView val = (((Object)(object)chest != (Object)null) ? chest.m_nview : null);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return false;
			}
			return ChestTake.CanServeSynchronously(val.HasOwner(), val.IsOwner());
		}

		private static int CountMatching(Inventory inventory, string sharedName)
		{
			if (inventory == null || string.IsNullOrEmpty(sharedName))
			{
				return 0;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val = allItems[i];
				if (val != null && val.m_shared != null && string.Equals(val.m_shared.m_name, sharedName, StringComparison.Ordinal))
				{
					num += val.m_stack;
				}
			}
			return num;
		}

		private static string PrefabName(ItemData item)
		{
			if (item == null)
			{
				return "<null>";
			}
			if ((Object)(object)item.m_dropPrefab != (Object)null)
			{
				return ((Object)item.m_dropPrefab).name;
			}
			if (item.m_shared == null)
			{
				return "<unknown>";
			}
			return item.m_shared.m_name;
		}
	}
}
namespace CraftFromDrawersAndChests.Toggle
{
	public static class StorageToggle
	{
		public static readonly int ZdoHash = StringExtensionMethods.GetStableHashCode("CFDAC_NoPull");

		public static bool IsOff(ZNetView view)
		{
			ZDO val = (((Object)(object)view != (Object)null) ? view.GetZDO() : null);
			if (val != null)
			{
				return val.GetBool(ZdoHash, false);
			}
			return false;
		}

		public static void Flip(ZNetView view)
		{
			if (!((Object)(object)view == (Object)null))
			{
				view.ClaimOwnership();
				ZDO zDO = view.GetZDO();
				if (zDO != null)
				{
					zDO.Set(ZdoHash, !zDO.GetBool(ZdoHash, false));
				}
			}
		}

		public static string HoverSuffix(bool off)
		{
			return ToggleRules.HoverSuffix(off);
		}

		public static string HoverText(ZNetView view)
		{
			if (!((Object)(object)view == (Object)null))
			{
				return HoverSuffix(IsOff(view)) + ToggleHint();
			}
			return string.Empty;
		}

		public static string ToggleHint()
		{
			return ToggleRules.ToggleHint(PluginConfig.Flags?.ToggleStorageKey);
		}

		public static bool IsToggleKeyPressed()
		{
			string[] array = ToggleRules.SplitKeys(PluginConfig.Flags?.ToggleStorageKey);
			if (array.Length == 0)
			{
				return false;
			}
			try
			{
				for (int i = 0; i < array.Length - 1; i++)
				{
					if (!Input.GetKey(array[i]))
					{
						return false;
					}
				}
				return Input.GetKeyDown(array[^1]);
			}
			catch
			{
				return false;
			}
		}
	}
	[HarmonyPatch]
	internal static class TogglePatches
	{
		[HarmonyPatch(typeof(Container), "GetHoverText")]
		[HarmonyPostfix]
		private static void ContainerGetHoverTextPostfix(Container __instance, ref string __result)
		{
			AppendState(VanillaChestSource.ViewOf(__instance), ref __result);
		}

		[HarmonyPatch(typeof(Player), "Update")]
		[HarmonyPostfix]
		private static void PlayerUpdatePostfix(Player __instance)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && StorageToggle.IsToggleKeyPressed())
			{
				ZNetView val = HoveredStorageView();
				if ((Object)(object)val != (Object)null)
				{
					StorageToggle.Flip(val);
				}
			}
		}

		private static ZNetView HoveredStorageView()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return null;
			}
			GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject();
			if ((Object)(object)hoverObject == (Object)null)
			{
				return null;
			}
			Container componentInParent = hoverObject.GetComponentInParent<Container>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				return VanillaChestSource.ViewOf(componentInParent);
			}
			ZNetView componentInParent2 = hoverObject.GetComponentInParent<ZNetView>();
			if (!((Object)(object)componentInParent2 != (Object)null) || !DrawerApi.IsDrawer(componentInParent2))
			{
				return null;
			}
			return componentInParent2;
		}

		private static void AppendState(ZNetView view, ref string text)
		{
			if (!((Object)(object)view == (Object)null))
			{
				text += StorageToggle.HoverText(view);
			}
		}
	}
	[HarmonyPatch]
	internal static class DrawerHoverTextPatch
	{
		private const string DrawerComponentFullName = "kg_ItemDrawers.DrawerComponent";

		[HarmonyPrepare]
		private static bool Prepare()
		{
			return DrawerComponentType() != null;
		}

		[HarmonyTargetMethod]
		private static MethodBase TargetMethod()
		{
			Type type = DrawerComponentType();
			if (!(type != null))
			{
				return null;
			}
			return AccessTools.Method(type, "GetHoverText", (Type[])null, (Type[])null);
		}

		[HarmonyPostfix]
		private static void Postfix(object __instance, ref string __result)
		{
			Component val = (Component)((__instance is Component) ? __instance : null);
			ZNetView val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ZNetView>() : null);
			if (!((Object)(object)val2 == (Object)null) && DrawerApi.IsDrawer(val2))
			{
				__result += StorageToggle.HoverText(val2);
			}
		}

		private static Type DrawerComponentType()
		{
			return AccessTools.TypeByName("kg_ItemDrawers.DrawerComponent");
		}
	}
}
namespace CraftFromDrawersAndChests.Storage
{
	[HarmonyPatch(typeof(Container))]
	internal static class ContainerTrackingPatches
	{
		private static readonly ManualLogSource Log = Logger.CreateLogSource("CraftFromDrawersAndChests");

		private static bool breadcrumbLogged;

		private const string BuiltPiecePrefix = "piece_";

		private const string ContainerNamePrefix = "Container";

		private static readonly int CreatorHash = StringExtensionMethods.GetStableHashCode("creator");

		private static readonly List<Container> TrackedContainers = new List<Container>();

		internal static IReadOnlyList<Container> Tracked => TrackedContainers;

		internal static void Clear()
		{
			TrackedContainers.Clear();
		}

		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		private static void AwakePostfix(Container __instance)
		{
			ZNetView val = VanillaChestSource.ViewOf(__instance);
			bool flag = ShouldTrack(__instance, val);
			if (!breadcrumbLogged)
			{
				breadcrumbLogged = true;
				Log.LogInfo((object)("CraftFromDrawersAndChests: tracking patch is live; first Container.Awake was '" + (((Object)(object)__instance != (Object)null && (Object)(object)((Component)__instance).gameObject != (Object)null) ? ((Object)__instance).name : "<null>") + "', inventory=" + ((Object)(object)__instance != (Object)null && __instance.GetInventory() != null) + ", zdo=" + ((Object)(object)val != (Object)null && val.GetZDO() != null) + " -> " + (flag ? "tracked" : "rejected")));
			}
			if (flag)
			{
				AddUnique(__instance);
			}
		}

		[HarmonyPatch("OnDestroyed")]
		[HarmonyPostfix]
		private static void OnDestroyedPostfix(Container __instance)
		{
			TrackedContainers.Remove(__instance);
		}

		internal static bool ShouldTrack(Container container, ZNetView view)
		{
			if ((Object)(object)container == (Object)null || (Object)(object)((Component)container).gameObject == (Object)null)
			{
				return false;
			}
			if (container.GetInventory() == null)
			{
				return false;
			}
			if ((Object)(object)view == (Object)null)
			{
				return false;
			}
			ZDO zDO = view.GetZDO();
			if (zDO == null)
			{
				return false;
			}
			string name = ((Object)container).name;
			if (name != null && (name.StartsWith("piece_", StringComparison.Ordinal) || name.StartsWith("Container", StringComparison.Ordinal)))
			{
				return true;
			}
			return zDO.GetLong(CreatorHash, 0L) != 0;
		}

		private static void AddUnique(Container container)
		{
			if (!TrackedContainers.Contains(container))
			{
				TrackedContainers.Add(container);
			}
		}
	}
	public readonly struct DrawerHandle
	{
		public readonly ZNetView View;

		public readonly string Prefab;

		public readonly int Amount;

		public readonly int Quality;

		public DrawerHandle(ZNetView view, string prefab, int amount, int quality)
		{
			View = view;
			Prefab = prefab;
			Amount = amount;
			Quality = quality;
		}
	}
	public static class DrawerApi
	{
		public const string PrefabKey = "Prefab";

		public const string AmountKey = "Amount";

		public const string QualityKey = "Quality";

		public const string DrawerComponentTypeName = "DrawerComponent";

		public const string ForceRemoveRpcName = "ForceRemove";

		public const string AddItemRequestRpcName = "AddItem_Request";

		private static readonly ManualLogSource Log = Logger.CreateLogSource("CraftFromDrawersAndChests");

		private static readonly DrawerHandle[] NoHandles = new DrawerHandle[0];

		private static readonly DrawerContract Contract = DrawerContract.Resolve(Type.GetType);

		private static readonly Type DrawerViewType = ResolveDrawerViewType();

		private static readonly FieldInfo DrawerViewField = FindZNetViewField(DrawerViewType);

		private static readonly FieldInfo DrawerPrefabField = FindField(DrawerViewType, "Prefab");

		private static readonly FieldInfo DrawerAmountField = FindField(DrawerViewType, "Amount");

		private static readonly FieldInfo DrawerQualityField = FindField(DrawerViewType, "Quality");

		public static bool IsAvailable => Contract.IsAvailable;

		public static IReadOnlyList<DrawerHandle> AllInRange(Vec3 origin, float range)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (!Contract.IsAvailable)
			{
				return NoHandles;
			}
			try
			{
				if (Contract.Kind == DrawerContractKind.ItemDrawersApi)
				{
					return FromItemDrawersApi(new Vector3(origin.X, origin.Y, origin.Z), range);
				}
				return FromClientSideV2(origin, range);
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Item Drawers query failed; treating as no drawers in range. " + ex));
				return NoHandles;
			}
		}

		public static bool IsDrawer(ZNetView view)
		{
			if ((Object)(object)view == (Object)null)
			{
				return false;
			}
			GameObject gameObject = ((Component)view).gameObject;
			if ((Object)(object)gameObject != (Object)null)
			{
				return (Object)(object)gameObject.GetComponent("DrawerComponent") != (Object)null;
			}
			return false;
		}

		private static IReadOnlyList<DrawerHandle> FromItemDrawersApi(Vector3 position, float range)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (!(Contract.RangeQuery.Invoke(null, new object[2] { position, range }) is IEnumerable enumerable))
			{
				return NoHandles;
			}
			List<DrawerHandle> list = new List<DrawerHandle>();
			foreach (object item in enumerable)
			{
				if (item != null)
				{
					ZNetView val = ReadView(item);
					if (!((Object)(object)val == (Object)null))
					{
						list.Add(new DrawerHandle(val, ReadString(DrawerPrefabField, item), ReadInt(DrawerAmountField, item), ReadInt(DrawerQualityField, item)));
					}
				}
			}
			return list;
		}

		private static IReadOnlyList<DrawerHandle> FromClientSideV2(Vec3 origin, float range)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: 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_006e: Unknown result type (might be due to invalid IL or missing references)
			if (!(Contract.AllDrawersQuery.Invoke(null, null) is IEnumerable enumerable))
			{
				return NoHandles;
			}
			List<DrawerHandle> list = new List<DrawerHandle>();
			foreach (object item in enumerable)
			{
				ZNetView val = (ZNetView)((item is ZNetView) ? item : null);
				if ((Object)(object)val == (Object)null || !val.IsValid())
				{
					continue;
				}
				Vector3 position = ((Component)val).transform.position;
				if (Eligibility.IsWithinRange(range, new Vec3(position.x, position.y, position.z).SqrDistanceTo(origin)))
				{
					ZDO zDO = val.GetZDO();
					if (zDO != null)
					{
						list.Add(new DrawerHandle(val, zDO.GetString("Prefab", string.Empty), zDO.GetInt("Amount", 0), zDO.GetInt("Quality", 1)));
					}
				}
			}
			return list;
		}

		private static Type ResolveDrawerViewType()
		{
			MethodInfo rangeQuery = Contract.RangeQuery;
			if (rangeQuery == null)
			{
				return null;
			}
			Type returnType = rangeQuery.ReturnType;
			if (returnType.IsArray)
			{
				return returnType.GetElementType();
			}
			if (returnType.IsGenericType)
			{
				return returnType.GetGenericArguments()[0];
			}
			return null;
		}

		private static FieldInfo FindZNetViewField(Type drawerViewType)
		{
			if (drawerViewType == null)
			{
				return null;
			}
			FieldInfo[] fields = drawerViewType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			for (int i = 0; i < fields.Length; i++)
			{
				if (fields[i].FieldType == typeof(ZNetView))
				{
					return fields[i];
				}
			}
			return null;
		}

		private static FieldInfo FindField(Type type, string name)
		{
			if (!(type != null))
			{
				return null;
			}
			return type.GetField(name);
		}

		private static ZNetView ReadView(object drawer)
		{
			if (!(DrawerViewField != null))
			{
				return null;
			}
			object? value = DrawerViewField.GetValue(drawer);
			return (ZNetView)((value is ZNetView) ? value : null);
		}

		private static string ReadString(FieldInfo field, object instance)
		{
			if (!(field != null))
			{
				return null;
			}
			return field.GetValue(instance) as string;
		}

		private static int ReadInt(FieldInfo field, object instance)
		{
			object obj = ((field != null) ? field.GetValue(instance) : null);
			if (!(obj is int))
			{
				return 0;
			}
			return (int)obj;
		}
	}
	public sealed class DrawerSource : IStorageSource
	{
		private static readonly int NoPullHash = StringExtensionMethods.GetStableHashCode("CFDAC_NoPull");

		private static readonly ManualLogSource Log = Logger.CreateLogSource("CraftFromDrawersAndChests");

		private readonly DrawerHandle _handle;

		public StorageKind Kind => StorageKind.Drawer;

		public Vec3 Position
		{
			get
			{
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				Vector3 position = ((Component)_handle.View).transform.position;
				return new Vec3(position.x, position.y, position.z);
			}
		}

		public string ObjectName
		{
			get
			{
				GameObject val = (((Object)(object)_handle.View != (Object)null) ? ((Component)_handle.View).gameObject : null);
				if (!((Object)(object)val != (Object)null))
				{
					return null;
				}
				return ((Object)val).name;
			}
		}

		public bool ToggledOff
		{
			get
			{
				ZDO val = (((Object)(object)_handle.View != (Object)null) ? _handle.View.GetZDO() : null);
				if (val != null)
				{
					return val.GetBool(NoPullHash, false);
				}
				return false;
			}
		}

		public bool IsValid
		{
			get
			{
				if ((Object)(object)_handle.View != (Object)null)
				{
					return _handle.View.IsValid();
				}
				return false;
			}
		}

		public DrawerSource(DrawerHandle handle)
		{
			_handle = handle;
		}

		public int Count(string itemName)
		{
			if (string.IsNullOrEmpty(itemName) || !IsValid)
			{
				return 0;
			}
			ZDO zDO = _handle.View.GetZDO();
			if (zDO == null)
			{
				return 0;
			}
			return DrawerStock.CountFor(zDO.GetString("Prefab", string.Empty), zDO.GetInt("Amount", 0), itemName);
		}

		public bool TryReadRaw(out string prefab, out int amount, out bool toggledOff)
		{
			prefab = null;
			amount = 0;
			toggledOff = false;
			if (!IsValid)
			{
				return false;
			}
			ZDO zDO = _handle.View.GetZDO();
			if (zDO == null)
			{
				return false;
			}
			prefab = zDO.GetString("Prefab", string.Empty);
			amount = zDO.GetInt("Amount", 0);
			toggledOff = zDO.GetBool(NoPullHash, false);
			return true;
		}

		public int Take(string itemName, int amount, Inventory destination)
		{
			int num = DrawerTake.AmountToRemove(amount, Count(itemName), destination != null);
			if (num <= 0)
			{
				return 0;
			}
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(itemName) : null);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			ItemData val3 = (((Object)(object)val2 != (Object)null) ? val2.m_itemData : null);
			if (val3 == null || val3.m_shared == null || val3.m_shared.m_maxStackSize <= 0)
			{
				Log.LogWarning((object)("Drawer '" + ObjectName + "' reports prefab '" + itemName + "' that has no usable ItemDrop; taking nothing."));
				return 0;
			}
			int maxStackSize = val3.m_shared.m_maxStackSize;
			string name = val3.m_shared.m_name;
			int quality = CurrentQuality();
			int num2 = (byte)Game.m_worldLevel;
			int num3 = 0;
			int num4;
			while ((num4 = DrawerTake.NextChunk(num, num3, maxStackSize)) > 0)
			{
				int matchingFreeStackSpace = DrawerTake.MatchingFreeStackSpace(name, quality, num2, DestinationStacks(destination));
				int emptySlots = DestinationEmptySlots(destination);
				if (!DrawerTake.CanDeliver(num4, maxStackSize, matchingFreeStackSpace, emptySlots))
				{
					Log.LogWarning((object)("No room in the destination inventory for the next " + num4 + "x '" + itemName + "'; delivered " + num3 + " of " + num + "."));
					break;
				}
				_handle.View.ClaimOwnership();
				_handle.View.InvokeRPC("ForceRemove", new object[1] { num4 });
				ItemData val4 = val3.Clone();
				val4.m_dropPrefab = val;
				val4.m_stack = num4;
				val4.m_quality = quality;
				val4.m_worldLevel = (byte)num2;
				if (!destination.AddItem(val4))
				{
					Log.LogError((object)("A drawer gave up " + num4 + "x '" + itemName + "' but the destination refused them; refunding them to the drawer."));
					Refund(itemName, num4, quality);
					break;
				}
				num3 += num4;
			}
			return num3;
		}

		private int CurrentQuality()
		{
			ZDO val = (((Object)(object)_handle.View != (Object)null) ? _handle.View.GetZDO() : null);
			int num = ((val == null) ? 1 : val.GetInt("Quality", 1));
			if (num >= 1)
			{
				return num;
			}
			return 1;
		}

		private static List<DrawerTake.InventoryStack> DestinationStacks(Inventory destination)
		{
			List<DrawerTake.InventoryStack> list = new List<DrawerTake.InventoryStack>();
			if (destination == null || destination.m_inventory == null)
			{
				return list;
			}
			for (int i = 0; i < destination.m_inventory.Count; i++)
			{
				ItemData val = destination.m_inventory[i];
				if (val != null && val.m_shared != null)
				{
					list.Add(new DrawerTake.InventoryStack(val.m_shared.m_name, val.m_quality, val.m_worldLevel, val.m_stack, val.m_shared.m_maxStackSize));
				}
			}
			return list;
		}

		private static int DestinationEmptySlots(Inventory destination)
		{
			if (destination == null || destination.m_inventory == null)
			{
				return 0;
			}
			return destination.m_width * destination.m_height - destination.m_inventory.Count;
		}

		private void Refund(string prefabName, int amount, int quality)
		{
			_handle.View.ClaimOwnership();
			_handle.View.InvokeRPC("AddItem_Request", new object[3] { prefabName, amount, quality });
		}
	}
	public interface IStorageSource
	{
		StorageKind Kind { get; }

		Vec3 Position { get; }

		string ObjectName { get; }

		bool ToggledOff { get; }

		bool IsValid { get; }

		int Count(string itemName);

		int Take(string itemName, int amount, Inventory destination);
	}
	public static class StorageRegistry
	{
		public static IReadOnlyList<IStorageSource> Collect(Vec3 origin, FeatureFlags flags)
		{
			List<IStorageSource> list = new List<IStorageSource>();
			if (flags == null)
			{
				return list;
			}
			ChestTypeRules chestRules = ChestTypeRules.FromFlags(flags);
			CollectChests(origin, flags, chestRules, list);
			CollectDrawers(origin, flags, chestRules, list);
			return list;
		}

		public static void Clear()
		{
			ContainerTrackingPatches.Clear();
		}

		private static void CollectChests(Vec3 origin, FeatureFlags flags, ChestTypeRules chestRules, List<IStorageSource> sources)
		{
			IReadOnlyList<Container> tracked = ContainerTrackingPatches.Tracked;
			for (int i = 0; i < tracked.Count; i++)
			{
				Container val = tracked[i];
				if (!((Object)(object)val == (Object)null))
				{
					VanillaChestSource vanillaChestSource = new VanillaChestSource(val);
					if (vanillaChestSource.IsValid && Eligibility.IsEligible(new StorageCandidate(StorageKind.Chest, vanillaChestSource.Position, vanillaChestSource.ObjectName, vanillaChestSource.ParentIsShip, vanillaChestSource.ParentIsWagon, vanillaChestSource.ToggledOff, vanillaChestSource.HasAccess, vanillaChestSource.HasInventory, vanillaChestSource.IsInsidePiece, vanillaChestSource.PassesGuardStone), origin, flags.MaxRange, flags.EnableChests, flags.EnableDrawers, chestRules))
					{
						sources.Add(vanillaChestSource);
					}
				}
			}
		}

		private static void CollectDrawers(Vec3 origin, FeatureFlags flags, ChestTypeRules chestRules, List<IStorageSource> sources)
		{
			IReadOnlyList<DrawerHandle> readOnlyList = DrawerApi.AllInRange(origin, flags.MaxRange);
			for (int i = 0; i < readOnlyList.Count; i++)
			{
				DrawerSource drawerSource = new DrawerSource(readOnlyList[i]);
				if (drawerSource.IsValid && Eligibility.IsEligible(new StorageCandidate(StorageKind.Drawer, drawerSource.Position, drawerSource.ObjectName, parentIsShip: false, parentIsWagon: false, drawerSource.ToggledOff, hasAccess: false, hasInventory: false, isInsidePiece: false, passesGuardStone: false), origin, flags.MaxRange, flags.EnableChests, flags.EnableDrawers, chestRules))
				{
					sources.Add(drawerSource);
				}
			}
		}
	}
	public sealed class VanillaChestSource : IStorageSource
	{
		private static readonly int NoPullHash = StringExtensionMethods.GetStableHashCode("CFDAC_NoPull");

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

		private static readonly ManualLogSource Log = Logger.CreateLogSource("CraftFromDrawersAndChests");

		private static bool accessWarningLogged;

		private readonly Container _container;

		public StorageKind Kind => StorageKind.Chest;

		public Vec3 Position
		{
			get
			{
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: 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_001d: Unknown result type (might be due to invalid IL or missing references)
				Vector3 position = ((Component)_container).transform.position;
				return new Vec3(position.x, position.y, position.z);
			}
		}

		public string ObjectName
		{
			get
			{
				GameObject val = (((Object)(object)_container != (Object)null) ? ((Component)_container).gameObject : null);
				if (!((Object)(object)val != (Object)null))
				{
					return null;
				}
				return ((Object)val).name;
			}
		}

		public bool ToggledOff
		{
			get
			{
				ZDO zdo = Zdo;
				if (zdo != null)
				{
					return zdo.GetBool(NoPullHash, false);
				}
				return false;
			}
		}

		public bool IsValid
		{
			get
			{
				if ((Object)(object)_container != (Object)null)
				{
					return (Object)(object)((Component)_container).gameObject != (Object)null;
				}
				return false;
			}
		}

		public bool CanTakeSynchronously => MultiUserChestBridge.CanRemoveSynchronously(_container);

		internal bool HasInventory
		{
			get
			{
				if ((Object)(object)_container != (Object)null)
				{
					return _container.GetInventory() != null;
				}
				return false;
			}
		}

		internal bool HasAccess
		{
			get
			{
				if ((Object)(object)_container == (Object)null)
				{
					return false;
				}
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					return false;
				}
				if (CheckAccessMethod == null)
				{
					if (!accessWarningLogged)
					{
						accessWarningLogged = true;
						Log.LogWarning((object)"Container.CheckAccess could not be reflected; no chest is treated as accessible.");
					}
					return false;
				}
				object obj = CheckAccessMethod.Invoke(_container, new object[1] { localPlayer.GetPlayerID() });
				if (obj is bool)
				{
					return (bool)obj;
				}
				return false;
			}
		}

		internal bool ParentIsShip
		{
			get
			{
				if ((Object)(object)_container == (Object)null)
				{
					return false;
				}
				Transform parent = ((Component)_container).transform.parent;
				if ((Object)(object)parent != (Object)null)
				{
					return (Object)(object)((Component)parent).GetComponent<Ship>() != (Object)null;
				}
				return false;
			}
		}

		internal bool ParentIsWagon
		{
			get
			{
				if ((Object)(object)_container != (Object)null)
				{
					return (Object)(object)_container.m_wagon != (Object)null;
				}
				return false;
			}
		}

		internal bool IsInsidePiece
		{
			get
			{
				if ((Object)(object)_container != (Object)null)
				{
					return (Object)(object)((Component)_container).GetComponentInParent<Piece>() != (Object)null;
				}
				return false;
			}
		}

		internal bool PassesGuardStone
		{
			get
			{
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_container == (Object)null)
				{
					return false;
				}
				if (_container.m_checkGuardStone)
				{
					return PrivateArea.CheckAccess(((Component)_container).transform.position, 0f, false, false);
				}
				return true;
			}
		}

		private ZDO Zdo
		{
			get
			{
				ZNetView val = ViewOf(_container);
				if (!((Object)(object)val != (Object)null))
				{
					return null;
				}
				return val.GetZDO();
			}
		}

		public VanillaChestSource(Container container)
		{
			_container = container;
		}

		public int Count(string itemName)
		{
			if (string.IsNullOrEmpty(itemName) || !IsValid)
			{
				return 0;
			}
			Inventory inventory = _container.GetInventory();
			if (inventory == null)
			{
				return 0;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val = allItems[i];
				if (val != null && !((Object)(object)val.m_dropPrefab == (Object)null) && string.Equals(((Object)val.m_dropPrefab).name, itemName, StringComparison.Ordinal))
				{
					num += val.m_stack;
				}
			}
			return num;
		}

		public int Take(string itemName, int amount, Inventory destination)
		{
			if (string.IsNullOrEmpty(itemName) || amount <= 0 || destination == null || !IsValid)
			{
				return 0;
			}
			Inventory inventory = _container.GetInventory();
			if (inventory == null)
			{
				return 0;
			}
			int i;
			int num;
			for (i = 0; i < amount; i += num)
			{
				ItemData val = FindLargestStack(inventory, itemName);
				if (val == null)
				{
					break;
				}
				num = MultiUserChestBridge.RemoveFromChest(_container, val, destination, amount - i);
				if (num == -1)
				{
					return -1;
				}
				if (num <= 0)
				{
					break;
				}
			}
			return i;
		}

		private static ItemData FindLargestStack(Inventory inventory, string itemName)
		{
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null)
			{
				return null;
			}
			ItemData val = null;
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val2 = allItems[i];
				if (val2 != null && !((Object)(object)val2.m_dropPrefab == (Object)null) && string.Equals(((Object)val2.m_dropPrefab).name, itemName, StringComparison.Ordinal) && (val == null || val2.m_stack > val.m_stack))
				{
					val = val2;
				}
			}
			return val;
		}

		internal static ZNetView ViewOf(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return null;
			}
			if ((Object)(object)container.m_rootObjectOverride != (Object)null)
			{
				return ((Component)container.m_rootObjectOverride).GetComponent<ZNetView>();
			}
			return ((Component)container).GetComponent<ZNetView>();
		}
	}
}
namespace CraftFromDrawersAndChests.Pull
{
	public static class PullEngine
	{
		private static readonly ManualLogSource Log = Logger.CreateLogSource("CraftFromDrawersAndChests");

		private static IReadOnlyList<IStorageSource> cachedSources;

		private static Vec3 cachedOrigin;

		private static FeatureFlags cachedFlags;

		private static int cachedFrame = -1;

		public static IReadOnlyList<IStorageSource> Collect(Vec3 origin, FeatureFlags flags)
		{
			int frameCount = Time.frameCount;
			if (cachedSources != null && frameCount == cachedFrame && cachedFlags == flags && cachedOrigin.X == origin.X && cachedOrigin.Y == origin.Y && cachedOrigin.Z == origin.Z)
			{
				return cachedSources;
			}
			cachedSources = StorageRegistry.Collect(origin, flags);
			cachedOrigin = origin;
			cachedFlags = flags;
			cachedFrame = frameCount;
			return cachedSources;
		}

		public static string PrefabName(Requirement requirement)
		{
			if (requirement == null || (Object)(object)requirement.m_resItem == (Object)null)
			{
				return null;
			}
			GameObject gameObject = ((Component)requirement.m_resItem).gameObject;
			if (!((Object)(object)gameObject != (Object)null))
			{
				return null;
			}
			return ((Object)gameObject).name;
		}

		public static PullRequest[] BuildShortfallRequests(Requirement[] requirements, int qualityLevel, int multiplier, Inventory inventory)
		{
			IReadOnlyDictionary<string, int> held = HeldCounts(requirements, inventory);
			int value;
			return BuildRequests(requirements, qualityLevel, multiplier, (string name) => held.TryGetValue(name, out value) ? value : 0, RequestBasis.Shortfall);
		}

		public static PullRequest[] BuildFullRequests(Requirement[] requirements, int qualityLevel, int multiplier)
		{
			return BuildRequests(requirements, qualityLevel, multiplier, null, RequestBasis.FullCost);
		}

		private static IReadOnlyDictionary<string, int> HeldCounts(Requirement[] requirements, Inventory inventory)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
			if (requirements == null || inventory == null)
			{
				return dictionary;
			}
			foreach (Requirement val in requirements)
			{
				if (val != null)
				{
					string text = PrefabName(val);
					if (!string.IsNullOrEmpty(text) && !dictionary.ContainsKey(text))
					{
						dictionary[text] = CountHeldBySharedName(inventory, SharedNameOf(val));
					}
				}
			}
			return dictionary;
		}

		public static PullRequest[] BuildRequests(Requirement[] requirements, int qualityLevel, int multiplier, Func<string, int> held, RequestBasis basis)
		{
			if (requirements == null)
			{
				return Array.Empty<PullRequest>();
			}
			List<PullRequest> list = new List<PullRequest>(requirements.Length);
			foreach (Requirement val in requirements)
			{
				if (val == null)
				{
					continue;
				}
				string text = PrefabName(val);
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				int num = val.GetAmount(qualityLevel) * multiplier;
				if (num > 0)
				{
					int held2 = held?.Invoke(text) ?? 0;
					int num2 = PullMath.RequestAmount(num, held2, basis);
					if (num2 > 0)
					{
						list.Add(new PullRequest(text, text, num2, val.m_upgraderResource));
					}
				}
			}
			return list.ToArray();
		}

		public static PullPlanResult PlanFor(Requirement[] requirements, int qualityLevel, int multiplier, Player player, FeatureFlags flags, out IReadOnlyList<IStorageSource> sources)
		{
			return PlanFor(requirements, qualityLevel, multiplier, ((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null, OriginOf(player), flags, AtUpgrader(player), out sources);
		}

		public static PullPlanResult PlanFor(Requirement[] requirements, int qualityLevel, int multiplier, Inventory inventory, Vec3 origin, FeatureFlags flags, bool allowUpgraderResources, out IReadOnlyList<IStorageSource> sources)
		{
			sources = Collect(origin, flags);
			return Plan(BuildShortfallRequests(requirements, qualityLevel, multiplier, inventory), sources, origin, flags, allowUpgraderResources);
		}

		public static PullPlanResult Plan(IReadOnlyList<PullRequest> requests, IReadOnlyList<IStorageSource> sources, Vec3 origin, FeatureFlags flags, bool allowUpgraderResources, bool fillUpTo = false)
		{
			IReadOnlyList<StorageStock> stocks = BuildStocks(sources, requests, origin);
			bool leaveOne = flags?.LeaveOne ?? false;
			bool byDistance = flags?.PullByDistance ?? false;
			return PullPlan.Plan(requests, stocks, leaveOne, byDistance, allowUpgraderResources, fillUpTo);
		}

		public static bool Execute(PullPlanResult plan, IReadOnlyList<IStorageSource> sources, Player player)
		{
			return Execute(plan, sources, ((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null);
		}

		public static bool Execute(PullPlanResult plan, IReadOnlyList<IStorageSource> sources, Inventory destination)
		{
			if (plan == null || !plan.Feasible)
			{
				return false;
			}
			if (destination == null)
			{
				return false;
			}
			IReadOnlyDictionary<string, IStorageSource> byId = ById(sources);
			return PullRunner.Run(plan.Lines, delegate(PullLine line, int amount)
			{
				if (!byId.TryGetValue(line.SourceId, out var value) || value == null || !value.IsValid)
				{
					ManualLogSource log = Log;
					string[] obj = new string[5] { "Pull: the source for ", null, null, null, null };
					int amount2 = line.Amount;
					obj[1] = amount2.ToString();
					obj[2] = "x '";
					obj[3] = line.ItemName;
					obj[4] = "' is gone before it could be taken; treating it as exhausted.";
					log.LogWarning((object)string.Concat(obj));
					return 0;
				}
				return value.Take(line.ItemName, amount, destination);
			}, delegate(string message)
			{
				Log.LogWarning((object)message);
			});
		}

		public static bool PullToPlayer(Requirement[] requirements, int qualityLevel, int multiplier, Player player, FeatureFlags flags, out bool requested)
		{
			return PullToInventoryCore(requirements, qualityLevel, multiplier, ((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null, OriginOf(player), flags, AtUpgrader(player), RequestBasis.FullCost, fillUpTo: true, out requested);
		}

		public static bool PullToInventory(Requirement[] requirements, int qualityLevel, int multiplier, Inventory destination, Vec3 origin, FeatureFlags flags, bool allowUpgraderResources, out bool requested)
		{
			return PullToInventoryCore(requirements, qualityLevel, multiplier, destination, origin, flags, allowUpgraderResources, RequestBasis.Shortfall, fillUpTo: false, out requested);
		}

		public static bool PullToInventoryFill(Requirement[] requirements, int qualityLevel, int multiplier, Inventory destination, Vec3 origin, FeatureFlags flags, bool allowUpgraderResources, out bool requested)
		{
			return PullToInventoryCore(requirements, qualityLevel, multiplier, destination, origin, flags, allowUpgraderResources, RequestBasis.Shortfall, fillUpTo: true, out requested);
		}

		private static bool PullToInventoryCore(Requirement[] requirements, int qualityLevel, int multiplier, Inventory destination, Vec3 origin, FeatureFlags flags, bool allowUpgraderResources, RequestBasis basis, bool fillUpTo, out bool requested)
		{
			requested = false;
			if (destination == null || flags == null)
			{
				return false;
			}
			IReadOnlyList<IStorageSource> sources = Collect(origin, flags);
			PullRequest[] requests = ((basis == RequestBasis.FullCost) ? BuildFullRequests(requirements, qualityLevel, multiplier) : BuildShortfallRequests(requirements, qualityLevel, multiplier, destination));
			if (fillUpTo)
			{
				requests = CapToDestination(requests, requirements, destination);
			}
			PullPlanResult pullPlanResult = Plan(requests, sources, origin, flags, allowUpgraderResources, fillUpTo);
			if (pullPlanResult.Feasible && pullPlanResult.Lines.Count == 0)
			{
				return true;
			}
			requested = true;
			if (!pullPlanResult.Feasible)
			{
				return false;
			}
			if (!DestinationAllows(destination, requirements, pullPlanResult, null))
			{
				return false;
			}
			return Execute(pullPlanResult, sources, destination);
		}

		private static PullRequest[] CapToDestination(PullRequest[] requests, Requirement[] requirements, Inventory destination)
		{
			if (requests == null || requests.Length == 0)
			{
				return requests;
			}
			List<DrawerTake.InventoryStack> list = null;
			int emptySlots = 0;
			PullRequest[] array = new PullRequest[requests.Length];
			for (int i = 0; i < requests.Length; i++)
			{
				PullRequest pullRequest = requests[i];
				ItemData val = ItemDataOf(FindRequirement(requirements, pullRequest.ItemName));
				if (val == null || val.m_shared == null || val.m_shared.m_maxStackSize <= 0)
				{
					array[i] = pullRequest;
					continue;
				}
				if (list == null)
				{
					list = InventoryStacks(destination);
					emptySlots = DestinationEmptySlots(destination);
				}
				DestinationCapacity.Delivery delivery = new DestinationCapacity.Delivery(val.m_shared.m_name, val.m_quality, val.m_worldLevel, pullRequest.Amount, val.m_shared.m_maxStackSize);
				int num = DestinationCapacity.AcceptableAmount(list, emptySlots, delivery, pullRequest.Amount);
				array[i] = ((num >= pullRequest.Amount) ? pullRequest : new PullRequest(pullRequest.Prefab, pullRequest.ItemName, num, pullRequest.IsUpgraderResource));
			}
			return array;
		}

		public static bool DestinationAllows(Player player, Requirement[] requirements, PullPlanResult plan, DestinationCapacity.Delivery? reserved)
		{
			return DestinationAllows(((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null, requirements, plan, reserved);
		}

		public static bool DestinationAllows(Inventory inventory, Requirement[] requirements, PullPlanResult plan, DestinationCapacity.Delivery? reserved)
		{
			if (plan == null)
			{
				return false;
			}
			if (inventory == null)
			{
				return false;
			}
			List<DestinationCapacity.Delivery> list = new List<DestinationCapacity.Delivery>(plan.Lines.Count + 1);
			if (reserved.HasValue)
			{
				list.Add(reserved.Value);
			}
			for (int i = 0; i < plan.Lines.Count; i++)
			{
				PullLine pullLine = plan.Lines[i];
				if (pullLine.Amount > 0)
				{
					ItemData val = ItemDataOf(FindRequirement(requirements, pullLine.ItemName));
					if (val == null || val.m_shared == null || val.m_shared.m_maxStackSize <= 0)
					{
						return false;
					}
					list.Add(new DestinationCapacity.Delivery(val.m_shared.m_name, val.m_quality, val.m_worldLevel, pullLine.Amount, val.m_shared.m_maxStackSize));
				}
			}
			return DestinationCapacity.CanAccept(InventoryStacks(inventory), DestinationEmptySlots(inventory), list);
		}

		private static Requirement FindRequirement(Requirement[] resources, string prefab)
		{
			if (resources == null || string.IsNullOrEmpty(prefab))
			{
				return null;
			}
			foreach (Requirement val in resources)
			{
				if (val != null && string.Equals(PrefabName(val), prefab, StringComparison.Ordinal))
				{
					return val;
				}
			}
			return null;
		}

		private static ItemData ItemDataOf(Requirement requirement)
		{
			if (requirement == null || (Object)(object)requirement.m_resItem == (Object)null)
			{
				return null;
			}
			return requirement.m_resItem.m_itemData;
		}

		public static string SharedNameOf(Requirement requirement)
		{
			ItemData val = ItemDataOf(requirement);
			if (val == null || val.m_shared == null)
			{
				return null;
			}
			return val.m_shared.m_name;
		}

		private static List<DrawerTake.InventoryStack> InventoryStacks(Inventory inventory)
		{
			List<DrawerTake.InventoryStack> list = new List<DrawerTake.InventoryStack>();
			if (inventory == null || inventory.m_inventory == null)
			{
				return list;
			}
			for (int i = 0; i < inventory.m_inventory.Count; i++)
			{
				ItemData val = inventory.m_inventory[i];
				if (val != null && val.m_shared != null)
				{
					list.Add(new DrawerTake.InventoryStack(val.m_shared.m_name, val.m_quality, val.m_worldLevel, val.m_stack, val.m_shared.m_maxStackSize));
				}
			}
			return list;
		}

		private static int DestinationEmptySlots(Inventory inventory)
		{
			if (inventory == null || inventory.m_inventory == null)
			{
				return 0;
			}
			return inventory.m_width * inventory.m_height - inventory.m_inventory.Count;
		}

		public static Vec3 OriginOf(Player player)
		{
			//IL_0024: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return new Vec3(0f, 0f, 0f);
			}
			Vector3 position = ((Component)player).transform.position;
			return new Vec3(position.x, position.y, position.z);
		}

		public static bool AtUpgrader(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			if ((Object)(object)currentCraftingStation != (Object)null)
			{
				return currentCraftingStation.m_upgrader;
			}
			return false;
		}

		public static int CountHeldBySharedName(Inventory inventory, string sharedName)
		{
			if (inventory == null || string.IsNullOrEmpty(sharedName))
			{
				return 0;
			}
			return inventory.CountItems(sharedName, -1, false);
		}

		private static IReadOnlyList<StorageStock> BuildStocks(IReadOnlyList<IStorageSource> sources, IReadOnlyList<PullRequest> requests, Vec3 origin)
		{
			List<StorageStock> list = new List<StorageStock>();
			if (sources == null || sources.Count == 0 || requests == null || requests.Count == 0)
			{
				return list;
			}
			string[] array = DistinctNames(requests);
			if (array.Length == 0)
			{
				return list;
			}
			for (int i = 0; i < sources.Count; i++)
			{
				IStorageSource storageSource = sources[i];
				if (storageSource == null || !storageSource.IsValid || !CanServeSynchronously(storageSource))
				{
					continue;
				}
				float distance = storageSource.Position.DistanceTo(origin);
				string sourceId = SourceId(i);
				for (int j = 0; j < array.Length; j++)
				{
					int num = storageSource.Count(array[j]);
					if (num > 0)
					{
						list.Add(new StorageStock(sourceId, array[j], num, distance));
					}
				}
			}
			return list;
		}

		public static bool CanServeSynchronously(IStorageSource source)
		{
			if (source is VanillaChestSource vanillaChestSource)
			{
				return vanillaChestSource.CanTakeSynchronously;
			}
			return true;
		}

		private static string[] DistinctNames(IReadOnlyList<PullRequest> requests)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			List<string> list = new List<string>();
			for (int i = 0; i < requests.Count; i++)
			{
				string itemName = requests[i].ItemName;
				if (!string.IsNullOrEmpty(itemName) && hashSet.Add(itemName))
				{
					list.Add(itemName);
				}
			}
			return list.ToArray();
		}

		private static string SourceId(int index)
		{
			return index.ToString(CultureInfo.InvariantCulture);
		}

		private static IReadOnlyDictionary<string, IStorageSource> ById(IReadOnlyList<IStorageSource> sources)
		{
			Dictionary<string, IStorageSource> dictionary = new Dictionary<string, IStorageSource>(StringComparer.Ordinal);
			if (sources != null)
			{
				for (int i = 0; i < sources.Count; i++)
				{
					dictionary[SourceId(i)] = sources[i];
				}
			}
			return dictionary;
		}
	}
}
namespace CraftFromDrawersAndChests.Patches
{
	[HarmonyPatch]
	internal static class CraftPatches
	{
		private static readonly ManualLogSource Log = Logger.CreateLogSource("CraftFromDrawersAndChests");

		[HarmonyPatch(typeof(Player), "HaveRequirementItems")]
		[HarmonyPostfix]
		private static void HaveRequirementItemsPostfix(Player __instance, Recipe piece, bool discover, int qualityLevel, int amount, ref bool __result)
		{
			if (!(__result || discover) && !((Object)(object)piece == (Object)null) && !((Object)(object)__instance == (Object)null) && Enabled() && PullKeyPatches.AllowByKey() && !piece.m_requireOnlyOneIngredient)
			{
				__result = CanReach(__instance, piece.m_resources, qualityLevel, amount, CraftedOutput(piece, qualityLevel, amount));
			}
		}

		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Piece),
			typeof(RequirementMode)
		})]
		[HarmonyPostfix]
		private static void HaveRequirementsPiecePostfix(Player __instance, Piece piece, RequirementMode mode, ref bool __result)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Invalid comparison between Unknown and I4
			if (!__result && !((Object)(object)piece == (Object)null) && !((Object)(object)__instance == (Object)null) && Enabled() && PullKeyPatches.AllowByKey() && ((int)mode == 0 || (int)mode == 2) && StationAndDlcAllow(__instance, piece, mode))
			{
				__result = (((int)mode == 2) ? HasAnyOfEach(__instance, piece.m_resources) : CanReach(__instance, piece.m_resources, 0, 1, null));
			}
		}

		[HarmonyPatch(typeof(Player), "ConsumeResources")]
		[HarmonyPrefix]
		private static bool ConsumeResourcesPrefix(Player __instance, Requirement[] requirements, int qualityLevel, int multiplier)
		{
			if ((Object)(object)__instance == (Object)null || requirements == null || !Enabled())
			{
				return true;
			}
			if (!PullKeyPatches.AllowByKey())
			{
				return true;
			}
			BuildPlan(__instance, requirements, qualityLevel, multiplier, out var sources, out var plan);
			if (!plan.Feasible)
			{
				Log.LogWarning((object)("Not consuming: the materials are no longer within range (" + string.Join(", ", plan.MissingItemNames) + ")."));
				return false;
			}
			if (plan.Lines.Count > 0 && !PullEngine.Execute(plan, sources, __instance))
			{
				Log.LogWarning((object)"Not consuming: a storage pull could not be completed, so the materials already moved are left in the inventory rather than eaten.");
				return false;
			}
			return true;
		}

		private static bool Enabled()
		{
			return PluginConfig.Flags?.ModEnabled ?? false;
		}

		private static bool CanReach(Player player, Requirement[] resources, int qualityLevel, int multiplier, DestinationCapacity.Delivery? reserved)
		{
			BuildPlan(player, resources, qualityLevel, multiplier, out var _, out var plan);
			if (plan.Feasible)
			{
				return DestinationAllows(player, resources, plan, reserved);
			}
			return false;
		}

		private static void BuildPlan(Player player, Requirement[] resources, int qualityLevel, int multiplier, out IReadOnlyList<IStorageSource> sources, out PullPlanResult plan)
		{
			plan = PullEngine.PlanFor(resources, qualityLevel, multiplier, player, PluginConfig.Flags, out sources);
		}

		private static bool DestinationAllows(Player player, Requirement[] resources, PullPlanResult plan, DestinationCapacity.Delivery? reserved)
		{
			return PullEngine.DestinationAllows(player, resources, plan, reserved);
		}

		private static DestinationCapacity.Delivery? CraftedOutput(Recipe piece, int qualityLevel, int amount)
		{
			if ((Object)(object)piece == (Object)null || (Object)(object)piece.m_item == (Object)null)
			{
				return null;
			}
			ItemData itemData = piece.m_item.m_itemData;
			if (itemData == null || itemData.m_shared == null || itemData.m_shared.m_maxStackSize <= 0)
			{
				return null;
			}
			int num = default(int);
			ItemData val = default(ItemData);
			int amount2 = piece.GetAmount(qualityLevel, ref num, ref val, amount);
			bool isUpgrade = DestinationCapacity.IsUpgrade(qualityLevel);
			InventoryGui instance = InventoryGui.instance;
			int perRollBonus = (((Object)(object)instance != (Object)null) ? instance.m_craftBonusAmount : 0);
			int num2 = DestinationCapacity.ReservedOutputAmount(amount2, perRollBonus, amount, isUpgrade);
			if (num2 <= 0)
			{
				return null;
			}
			return new DestinationCapacity.Delivery(itemData.m_shared.m_name, qualityLevel, (byte)Game.m_worldLevel, num2, itemData.m_shared.m_maxStackSize);
		}

		private static bool HasAnyOfEach(Player player, Requirement[] resources)
		{
			if (resources == null)
			{
				return true;
			}
			FeatureFlags flags = PluginConfig.Flags;
			IReadOnlyList<IStorageSource> sources = PullEngine.Collect(PullEngine.OriginOf(player), flags);
			Inventory inventory = ((Humanoid)player).GetInventory();
			foreach (Requirement val in resources)
			{
				if (val != null && !((Object)(object)val.m_resItem == (Object)null) && val.m_amount > 0)
				{
					string text = PullEngine.PrefabName(val);
					if (!string.IsNullOrEmpty(text) && PullEngine.CountHeldBySharedName(inventory, PullEngine.SharedNameOf(val)) <= 0 && !AnySourceHolds(sources, text))
					{
						return false;
					}
				}
			}
			return true;
		}

		private static bool AnySourceHolds(IReadOnlyList<IStorageSource> sources, string prefab)
		{
			for (int i = 0; i < sources.Count; i++)
			{
				IStorageSource storageSource = sources[i];
				if (storageSource != null && storageSource.IsValid && PullEngine.CanServeSynchronously(storageSource) && storageSource.Count(prefab) > 0)
				{
					return true;
				}
			}
			return false;
		}

		private static bool StationAndDlcAllow(Player player, Piece piece, RequirementMode mode)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			CraftingStation craftingStation = piece.m_craftingStation;
			if ((Object)(object)craftingStation != (Object)null)
			{
				if ((int)mode == 2 || (int)mode == 1)
				{
					if (player.m_knownStations == null || !player.m_knownStations.ContainsKey(craftingStation.m_name))
					{
						return false;
					}
				}
				else if ((Object)(object)CraftingStation.HaveBuildStationInRange(craftingStation.m_name, ((Component)player).transform.position) == (Object)null && !ZoneSystem.instance.GetGlobalKey((GlobalKeys)27))
				{
					return false;
				}
			}
			string dlc = piece.m_dlc;
			if (!string.IsNullOrEmpty(dlc) && (Object)(object)DLCMan.instance != (Object)null && !DLCMan.instance.IsDLCInstalled(dlc))
			{
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch]
	internal static class PullKeyPatches
	{
		private static readonly ManualLogSource Log = Logger.CreateLogSource("CraftFromDrawersAndChests");

		private const string ResAmountChild = "res_amount";

		private static bool lastAllowed = true;

		private static readonly Dictionary<string, string> readoutLogged = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly FieldInfo PlacementStatusField = AccessTools.Field(typeof(Player), "m_placementStatus");

		[HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")]
		[HarmonyPrefix]
		private static bool OnCraftPressedPrefix(RecipeDataPair ___m_selectedRecipe, ItemData ___m_craftUpgradeItem, bool ___m_multiCrafting, int ___m_multiCraftAmount)
		{
			FeatureFlags flags = PluginConfig.Flags;
			if (flags == null || !flags.ModEnabled)
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return true;
			}
			Recipe recipe = ((RecipeDataPair)(ref ___m_selectedRecipe)).Recipe;
			if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null)
			{
				return true;
			}
			if (!PullKeyRules.ShouldPull(KeyHeld(flags.PullItemsKey), PreventKeyHeld(flags), flags.SwitchPrevent))
			{
				return true;
			}
			int num = ((___m_craftUpgradeItem == null) ? 1 : (___m_craftUpgradeItem.m_quality + 1));
			if (num > recipe.m_item.m_itemData.m_shared.m_maxQuality)
			{
				return true;
			}
			int multiplier = ((!___m_multiCrafting || ___m_multiCraftAmount <= 0) ? 1 : ___m_multiCraftAmount);
			if (!PullEngine.PullToPlayer(recipe.m_resources, num, multiplier, localPlayer, flags, out var requested))
			{
				ShowMessage(localPlayer, FailureReason(localPlayer, recipe.m_resources, num, multiplier, flags));
				return false;
			}
			if (!requested)
			{
				string text = MissingReason(localPlayer, recipe.m_resources, num, multiplier, flags);
				if (text != null)
				{
					ShowMessage(localPlayer, text);
				}
				return false;
			}
			ShowMessage(localPlayer, flags.PulledMessage);
			Debug(flags, "Pull: pulled the ingredients for '" + recipe.m_item.m_itemData.m_shared.m_name + "'.");
			return false;
		}

		[HarmonyPatch(typeof(Player), "UpdatePlacement")]
		[HarmonyPrefix]
		private static bool UpdatePlacementPrefix(Player __instance, bool takeInput, PieceTable ___m_buildPieces, GameObject ___m_placementGhost)
		{
			FeatureFlags flags = PluginConfig.Flags;
			if (flags == null || (Object)(object)__instance == (Object)null || __instance != Player.m_localPlayer)
			{
				return true;
			}
			if (!BuildPullRules.ShouldIntercept(flags.ModEnabled, KeyHeld(flags.PullItemsKey), PreventKeyHeld(flags), flags.SwitchPrevent, ((Character)__instance).InPlaceMode(), takeInput, Hud.IsPieceSelectionVisible()))
			{
				return true;
			}
			if (!ZInput.GetButtonDown("Attack") && !ZInput.GetButtonDown("JoyPlace"))
			{
				return true;
			}
			Piece val = (((Object)(object)___m_buildPieces != (Object)null) ? ___m_buildPieces.GetSelectedPiece() : null);
			switch (BuildPullRules.Decide((Object)(object)val != (Object)null, (Object)(object)val != (Object)null && val.m_repairPiece, (Object)(object)___m_placementGhost != (Object)null, BuildPullRules.IsPlacementValid(PlacementStatus(__instance))))
			{
			case BuildPullDecision.LetVanillaHandle:
				return true;
			case BuildPullDecision.RefuseWithoutPull:
				return false;
			default:
			{
				if (!PullEngine.PullToPlayer(val.m_resources, 0, 1, __instance, flags, out var requested))
				{
					ShowMessage(__instance, FailureReason(__instance, val.m_resources, 0, 1, flags));
					return false;
				}
				if (!requested)
				{
					string text = MissingReason(__instance, val.m_resources, 0, 1, flags);
					if (text != null)
					{
						ShowMessage(__instance, text);
					}
					return false;
				}
				ShowMessage(__instance, flags.PulledMessage);
				Debug(flags, "Pull: pulled the materials for building piece '" + ((Object)val).name + "'.");
				return false;
			}
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
		[HarmonyPostfix]
		private static void SetupRequirementPostfix(Transform elementRoot, Requirement req, Player player, int quality, int craftMultiplier)
		{
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			FeatureFlags flags = PluginConfig.Flags;
			if (flags == null || !flags.ModEnabled || !PullKeyRules.AllowByKey(flags.SwitchPrevent, PreventKeyHeld(flags)) || req == null || (Object)(object)req.m_resItem == (Object)null || (Object)(object)elementRoot == (Object)null || (Object)(object)player == (Object)null)
			{
				return;
			}
			string text = PullEngine.PrefabName(req);
			string text2 = PullEngine.SharedNameOf(req);
			if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2))
			{
				return;
			}
			int num = req.GetAmount(quality) * craftMultiplier;
			if (num <= 0)
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return;
			}
			int num2 = PullEngine.CountHeldBySharedName(inventory, text2);
			int num3 = 0;
			IReadOnlyList<IStorageSource> readOnlyList = null;
			bool flag = num2 < num;
			if (flag)
			{
				readOnlyList = PullEngine.Collect(PullEngine.OriginOf(player), flags);
				for (int i = 0; i < readOnlyList.Count; i++)
				{
					IStorageSource storageSource = readOnlyList[i];
					if (storageSource != null && storageSource.IsValid && PullEngine.CanServeSynchronously(storageSource))
					{
						num3 += storageSource.Count(text);
					}
				}
			}
			int num4 = num2 + num3;
			LogReadout(flags, text, text2, num, num2, num3, readOnlyList, player);
			Transform val = elementRoot.Find("res_amount");
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			TMP_Text component = ((Component)val).GetComponent<TMP_Text>();
			if (!((Object)(object)component == (Object)null))
			{
				if (flag && num4 >= num)
				{
					((Graphic)component).color = ((Mathf.Sin(Time.time * 10f) > 0f) ? PluginConfig.FlashColor : PluginConfig.UnFlashColor);
				}
				component.text = PullKeyRules.ResourceAmountText(flags.ResourceString, num4, num);
			}
		}

		private static void LogReadout(FeatureFlags flags, string prefab, string sharedName, int amount, int held, int fromStorage, IReadOnlyList<IStorageSource> sources, Player player)
		{
			if (flags == null || !flags.IsDebug)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("Readout '").Append(prefab).Append("' (")
				.Append(sharedName)
				.Append("): held ")
				.Append(held)
				.Append(" + storage ")
				.Append(fromStorage)
				.Append(" of ")
				.Append(amount)
				.Append(" required");
			if (held < amount && fromStorage == 0 && sources != null)
			{
				Vec3 other = PullEngine.OriginOf(player);
				stringBuilder.Append(" | nothing holds it; ").Append(sources.Count).Append(" source(s) in range:");
				for (int i = 0; i < sources.Count; i++)
				{
					IStorageSource storageSource = sources[i];
					if (storageSource != null && storageSource.IsValid)
					{
						stringBuilder.Append(" [").Append(storageSource.Kind).Append(' ')
							.Append(storageSource.ObjectName)
							.Append(' ')
							.Append(Mathf.RoundToInt(storageSource.Position.DistanceTo(other)))
							.Append("m");
						if (storageSource.ToggledOff)
						{
							stringBuilder.Append(" OFF");
						}
						stringBuilder.Append(" count=").Append(storageSource.Count(prefab));
						if (storageSource is DrawerSource drawerSource && drawerSource.TryReadRaw(out var prefab2, out var amount2, out var _))
						{
							stringBuilder.Append(" zdo='").Append(prefab2).Append("'x")
								.Append(amount2);
						}
						stringBuilder.Append(']');
					}
				}
			}
			string text = stringBuilder.ToString();
			if (!readoutLogged.TryGetValue(prefab, out var value) || !(value == text))
			{
				readoutLogged[prefab] = text;
				Log.LogInfo((object)text);
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "Update")]
		[HarmonyPrefix]
		private static void InventoryGuiUpdatePrefix(InventoryGui __instance, Animator ___m_animator)
		{
			bool num = AllowByKey();
			bool flag = num != lastAllowed;
			lastAllowed = num;
			if (flag && !((Object)(object)__instance == (Object)null) && !((Object)(object)___m_animator == (Object)null) && ___m_animator.GetBool("visible") && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", new Type[1] { typeof(bool) }, (Type[])null);
				if (!(methodInfo == null))
				{
					methodInfo.Invoke(__instance, new object[1] { false });
				}
			}
		}

		private static string FailureReason(Player player, Requirement[] requirements, int qualityLevel, int multiplier, FeatureFlags flags)
		{
			return MissingReason(player, requirements, qualityLevel, multiplier, flags) ?? "Not enough room in your inventory to pull these materials.";
		}

		private static string MissingReason(Player player, Requirement[] requirements, int qualityLevel, int multiplier, FeatureFlags flags)
		{
			IReadOnlyList<IStorageSource> sources;
			PullPlanResult pullPlanResult = PullEngine.PlanFor(requirements, qualityLevel, multiplier, player, flags, out sources);
			if (!pullPlanResult.Feasible)
			{
				return PullKeyRules.MissingMessage(pullPlanResult.MissingItemNames);
			}
			return null;
		}

		private static int PlacementStatus(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return -1;
			}
			return BuildPullRules.PlacementStatusFrom(PlacementStatusField != null, (PlacementStatusField != null) ? PlacementStatusField.GetValue(player) : null);
		}

		private static bool PreventKeyHeld(FeatureFlags flags)
		{
			if (flags != null)
			{
				return KeyHeld(flags.PreventModKey);
			}
			return false;
		}

		internal static bool AllowByKey()
		{
			FeatureFlags flags = PluginConfig.Flags;
			if (flags == null || !flags.ModEnabled)
			{
				return false;
			}
			return PullKeyRules.AllowByKey(flags.SwitchPrevent, KeyHeld(flags.PreventModKey));
		}

		internal static bool KeyHeld(string key)
		{
			if (!ToggleRules.IsKeyConfigured(key))
			{
				return false;
			}
			try
			{
				return Input.GetKey(ToggleRules.InputKeyName(key));
			}
			catch
			{
				return false;
			}
		}

		private static void ShowMessage(Player player, string message)
		{
			if (!((Object)(object)player == (Object)null) && !string.IsNullOrEmpty(message))
			{
				((Character)player).Message((MessageType)2, message, 0, (Sprite)null, false);
			}
		}

		private static void Debug(FeatureFlags flags, string message)
		{
			if (flags != null && flags.IsDebug)
			{
				Log.LogInfo((object)message);
			}
		}
	}
	[HarmonyPatch]
	internal static class StationPatches
	{
		private enum PullSetting
		{
			Fuel,
			Ore,
			Cooking
		}

		private static readonly ManualLogSource Log = Logger.CreateLogSource("CraftFromDrawersAndChests");

		private static readonly MethodInfo SmelterGetQueueSize = Resolve(typeof(Smelter), "GetQueueSize");

		private static readonly MethodInfo SmelterGetFuel = Resolve(typeof(Smelter), "GetFuel");

		private static readonly MethodInfo CookingStationGetFuel = Resolve(typeof(CookingStation), "GetFuel");

		private static readonly MethodInfo CookingStationHaveDoneItem = Resolve(typeof(CookingStation), "HaveDoneItem");

		private static readonly MethodInfo CookingStationGetFreeSlot = Resolve(typeof(CookingStation), "GetFreeSlot");

		private static readonly MethodInfo CookingStationIsFireLit = Resolve(typeof(CookingStation), "IsFireLit");

		private static bool _invokeFailureLogged;

		private static bool _returnTypeFailureLogged;

		private const string AddFuelRpc = "RPC_AddFuel";

		private const string AddOreRpc = "RPC_AddOre";

		private const string AddFuelHintToken = "$piece_smelter_add";

		private static MethodInfo Resolve(Type type, string name)
		{
			MethodInfo methodInfo;
			try
			{
				methodInfo = AccessTools.Method(type, name, (Type[])null, (Type[])null);
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("StationPatches: resolving vanilla member " + type.Name + "." + name + "() threw; stations of this kind will not be fed from storage. " + ex.Message));
				return null;
			}
			if (methodInfo == null)
			{
				Log.LogWarning((object)("StationPatches: vanilla member " + type.Name + "." + name + "() was not found; stations of this kind will not be fed from storage."));
			}
			return methodInfo;
		}

		private static bool TryInvoke(MethodInfo method, object instance, out object result)
		{
			result = null;
			if (method == null || instance == null)
			{
				return false;
			}
			try
			{
				result = method.Invoke(instance, null);
				return true;
			}
			catch (Exception ex)
			{
				if (!_invokeFailureLogged)
				{
					_invokeFailureLogged = true;
					Log.LogWarning((object)("StationPatches: a vanilla station member threw on invoke; affected stations will not be fed. " + ex.Message));
				}
				return false;
			}
		}

		private static void LogUnexpectedReturnType(string expected, object actual)
		{
			if (!_returnTypeFailureLogged)
			{
				_returnTypeFailureLogged = true;
				Log.LogWarning((object)("StationPatches: a vanilla station member returned " + ((actual == null) ? "null" : actual.GetType().Name) + " where " + expected + " was expected; affected stations will not be fed. Vanilla may have changed shape."));
			}
		}

		private static bool TryInvokeInt(MethodInfo method, object instance, out int value)
		{
			value = 0;
			if (!TryInvoke(method, instance, out var result))
			{
				return false;
			}
			if (!(result is int))
			{
				LogUnexpectedReturnType("int", result);
				return false;
			}
			value = (int)result;
			return true;
		}

		private static bool TryInvokeFloat(MethodInfo method, object instance, out float value)
		{
			value = 0f;
			if (!TryInvoke(method, instance, out var result))
			{
				return false;
			}
			if (!(result is float))
			{
				LogUnexpectedReturnType("float", result);
				return false;
			}
			value = (float)result;
			return true;
		}

		private static bool TryInvokeBool(MethodInfo method, object instance, out bool value)
		{
			value = false;
			if (!TryInvoke(method, instance, out var result))
			{
				return false;
			}
			if (!(result is bool))
			{
				LogUnexpectedReturnType("bool", result);
				return false;
			}
			value = (bool)result;
			return true;
		}

		[HarmonyPatch(typeof(Fireplace), "Interact")]
		[HarmonyPrefix]
		private static bool FireplaceInteractPrefix(Fireplace __instance, Humanoid user, bool hold, ZNetView ___m_nview)
		{
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || (Object)(object)___m_nview == (Object)null || hold)
			{
				return true;
			}
			FeatureFlags flags = Enabled(PullSetting.Fuel);
			if (flags == null)
			{
				return true;
			}
			Player player = (Player)(object)((user is Player) ? user : null);
			Inventory val = (((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null);
			if (val == null)
			{
				return true;
			}
			ItemDrop fuelItem = __instance.m_fuelItem;
			if (!HasItemData(fuelItem))
			{
				return true;
			}
			bool fillAll = FillAllHeld(flags);
			if (!fillAll && val.HaveItem(SharedName(fuelItem), true))
			{
				return true;
			}
			ZDO zdo = ___m_nview.GetZDO();
			float fuel = ((zdo != null) ? zdo.GetFloat(ZDOVars.s_fuel, 0f) : 0f);
			int num = StationFeedRules.RemainingFuelCapacity(__instance.m_maxFuel, fuel);
			if (num <= 0)
			{
				return true;
			}
			string prefab = PrefabName(fuelItem);
			if (StationFeedRules.IsDisallowedType(flags.FuelDisallowTypes, prefab))
			{
				return true;
			}
			Vector3 origin = ((Component)__instance).transform.position;
			return FeedStation(player, val, ___m_nview, flags, StationFeedRules.FillAmount(fillAll, num), "RPC_AddFuel", (int wanted) => (!PullFromStorage(player, origin, prefab, fuelItem, flags, wanted, fillAll)) ? null : prefab, () => FuelRemaining(__instance, () => (zdo == null) ? 0f : zdo.GetFloat(ZDOVars.s_fuel, 0f))) <= 0;
		}

		[HarmonyPatch(typeof(Smelter), "OnAddOre")]
		[HarmonyPrefix]
		private static bool SmelterOnAddOrePrefix(Smelter __instance, Humanoid user, ItemData item, ZNetView ___m_nview, ref bool __result)
		{
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || item != null)
			{
				return true;
			}
			FeatureFlags flags = Enabled(PullSetting.Ore);
			if (flags == null)
			{
				return true;
			}
			Player player = (Player)(object)((user is Player) ? user : null);
			Inventory inventory = (((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null);
			if (inventory == null)
			{
				return true;
			}
			if (!TryInvokeInt(SmelterGetQueueSize, __instance, out var value))
			{
				return true;
			}
			int num = StationFeedRules.RemainingCapacity(__instance.m_maxOre, value);
			if (num <= 0)
			{
				return true;
			}
			bool fillAll = FillAllHeld(flags);
			List<ItemDrop> inputs = InputItems(__instance.m_conversion);
			if (InventoryHasInput(inventory, inputs))
			{
				return true;
			}
			Vector3 origin = ((Component)__instance).transform.position;
			int num2 = FeedStation(player, inventory, ___m_nview, flags, StationFeedRules.FillAmount(fillAll, num), "RPC_AddOre", (int wanted) => (!PullFirstInputs(player, origin, inputs, flags.OreDisallowTypes, flags, wanted, fillAll)) ? null : FirstInputInInventory(inventory, inputs), () => QueueRemaining(__instance));
			if (num2 > 0)
			{
				__result = true;
			}
			return num2 <= 0;
		}

		private static string FirstInputInInventory(Inventory inventory, List<ItemDrop> inputs)
		{
			if (inventory == null || inputs == null)
			{
				return null;
			}
			string text = null;
			for (int i = 0; i < inputs.Count; i++)
			{
				ItemDrop item = inputs[i];
				if (HasItemData(item))
				{
					text = SharedName(item);
					if (inventory.GetItem(text, -1, false) != null)
					{
						return PrefabName(item);
					}
				}
			}
			return null;
		}

		[HarmonyPatch(typeof(Smelter), "OnAddFuel")]
		[HarmonyPrefix]
		private static bool SmelterOnAddFuelPrefix(Smelter __instance, Humanoid user, ItemData item, ZNetView ___m_nview, ref bool __result)
		{
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || item != null)
			{
				return true;
			}
			FeatureFlags flags = Enabled(PullSetting.Fuel);
			if (flags == null)
			{
				return true;
			}
			Player player = (Player)(object)((user is Player) ? user : null);
			Inventory val = (((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null);
			if (val == null)
			{
				return true;
			}
			if (!TryInvokeFloat(SmelterGetFuel, __instance, out var value))
			{
				return true;
			}
			int num = StationFeedRules.RemainingFuelCapacity(__instance.m_maxFuel, value);
			if (num <= 0)
			{
				return true;
			}
			ItemDrop fuelItem = __instance.m_fuelItem;
			if (!HasItemData(fuelItem))
			{
				return true;
			}
			bool fillAll = FillAllHeld(flags);
			if (!fillAll && val.HaveItem(SharedName(fuelItem), true))
			{
				return true;
			}
			string prefab = PrefabName(fuelItem);
			if (StationFeedRules.IsDisallowedType(flags.FuelDisallowTypes, prefab))
			{
				return true;
			}
			Vector3 origin = ((Component)__instance).transform.position;
			int num2 = FeedStation(player, val, ___m_nview, flags, StationFeedRules.FillAmount(fillAll, num), "RPC_AddFuel", (int wanted) => (!PullFromStorage(player, origin, prefab, fuelItem, flags, wanted, fillAll)) ? null : prefab, () => SmelterFuelRemaining(__instance));
			if (num2 > 0)
			{
				__result = true;
			}
			return num2 <= 0;
		}

		[HarmonyPatch(typeof(CookingStation), "OnAddFuelSwitch")]
		[HarmonyPrefix]
		private static bool CookingStationOnAddFuelSwitchPrefix(CookingStation __instance, Humanoid user, ItemData item, ZNetView ___m_nview, ref bool __result)
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || item != null)
			{
				return true;
			}
			FeatureFlags flags = Enabled(PullSetting.Fuel);
			if (flags == null)
			{
				return true;
			}
			Player player = (Player)(object)((user is Player) ? user : null);
			Inventory val = (((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null);
			if (val == null)
			{
				return true;
			}
			if (!TryInvokeFloat(CookingStationGetFuel, __instance, out var value) || StationFeedRules.RemainingFuelCapacity(__instance.m_maxFuel, value) <= 0)
			{
				return true;
			}
			ItemDrop fuelItem = __instance.m_fuelItem;
			if (!HasItemData(fuelItem))
			{
				return true;
			}
			if (val.HaveItem(SharedName(fuelItem), true))
			{
				return true;
			}
			string prefab = PrefabName(fuelItem);
			if (StationFeedRules.IsDisallowedType(flags.FuelDisallowTypes, prefab))
			{
				return true;
			}
			Vector3 origin = ((Component)__instance).transform.position;
			int num = FeedStation(player, val, ___m_nview, flags, 1, "RPC_AddFuel", (int wanted) => (!PullFromStorage(player, origin, prefab, fuelItem, flags, wanted, fillUpTo: false)) ? null : prefab, () => CookingFuelRemaining(__instance));
			if (num > 0)
			{
				__result = true;
			}
			return num <= 0;
		}

		private static int FuelRemaining(Fireplace fireplace, Func<float> readFuel)
		{
			if ((Object)(object)fireplace == (Object)null || readFuel == null)
			{
				return -1;
			}
			return StationFeedRules.RemainingFuelCapacity(fireplace.m_maxFuel, readFuel());
		}

		private static int QueueRemaining(Smelter smelter)
		{
			if ((Object)(object)smelter == (Object)null || !TryInvokeInt(SmelterGetQueueSize, smelter, out var value))
			{
				return -1;
			}
			return StationFeedRules.RemainingCapacity(smelter.m_maxOre, value);
		}

		private static int SmelterFuelRemaining(Smelter smelter)
		{
			if ((Object)(object)smelter == (Object)null || !TryInvokeFloat(SmelterGetFuel, smelter, out var value))
			{
				return -1;
			}
			return StationFeedRules.RemainingFuelCapacity(smelter.m_maxFuel, value);
		}

		private static int CookingFuelRemaining(CookingStation station)
		{
			if ((Object)(object)station == (Object)null || !TryInvokeFloat(CookingStationGetFuel, station, out var value))
			{
				return -1;
			}
			return StationFeedRules.RemainingFuelCapacity(station.m_maxFuel, value);
		}

		[HarmonyPatch(typeof(CookingStation), "OnInteract")]
		[HarmonyPrefix]
		private static bool CookingStationOnInteractPrefix(CookingStation __instance, Humanoid user)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			FeatureFlags featureFlags = Enabled(PullSetting.Cooking);
			if (featureFlags == null)
			{
				return true;
			}
			if (!TryInvokeBool(CookingStationHaveDoneItem, __instance, out var value) || value)
			{
				return true;
			}
			Player val = (Player)(object)((user is Player) ? user : null);
			Inventory val2 = (((Object)(object)val != (Object)null) ? ((Humanoid)val).GetInventory() : null);
			if (val2 == null)
			{
				return true;
			}
			if (!TryInvokeInt(CookingStationGetFreeSlot, __instance, out var value2) || value2 == -1)
			{
				return true;
			}
			if (__instance.m_requireFire && (!TryInvokeBool(CookingStationIsFireLit, __instance, out var value3) || !value3))
			{
				return true;
			}
			List<ItemDrop> inputs = InputItems(__instance.m_conversion);
			if (InventoryHasInput(val2, inputs))
			{
				return true;
			}
			PullFirstInputs(val, ((Component)__instance).transform.position, inputs, featureFlags.OreDisallowTypes, featureFlags, 1, fillUpTo: false);
			return true;
		}

		[HarmonyPatch(typeof(Turret), "UseItem")]
		[HarmonyPrefix]
		private static bool TurretUseItemPrefix(Turret __instance, Humanoid user, ItemData item)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || item != null)
			{
				return true;
			}
			FeatureFlags flags = PluginConfig.Flags;
			if (flags == null || !flags.ModEnabled)
			{
				return true;
			}
			if (!PullKeyPatches.AllowByKey())
			{
				return true;
			}
			Player val = (Player)(object)((user is Player) ? user : null);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			if (StationFeedRules.RemainingCapacity(__instance.m_maxAmmo, __instance.GetAmmo()) <= 0)
			{
				return true;
			}
			string ammoType = __instance.GetAmmoType();
			if (string.IsNullOrEmpty(ammoType))
			{
				return true;
			}
			ItemDrop val2 = AmmoItem(ammoType);
			if ((Object)(object)val2 == (Object)null)
			{
				Debug(flags, "Turret: no item prefab for ammunition type '" + ammoType + "'; nothing pulled.");
				return true;
			}
			PullFromStorage(val, ((Component)__instance).transform.position, ammoType, val2, flags, 1, fillUpTo: false);
			return true;
		}

		private static int FeedStation(Player player, Inventory inventory, ZNetView nview, FeatureFlags flags, int remaining, string rpcName, Func<int, string> pullOneBatch, Func<int> remainingNow)
		{
			if ((Object)(object)player == (Object)null || inventory == null || (Object)(object)nview == (Object)null || flags == null || remaining <= 0 || pullOneBatch == null)
			{
				return 0;
			}
			int i;
			int num;
			for (i = 0; i < remaining; i += num)
			{
				string text = pullOneBatch(remaining - i);
				if (text == null)
				{
					break;
				}
				num = FeedStationFromInventory(inventory, nview, text, remaining - i, rpcName, remainingNow);
				if (num <= 0)
				{
					break;
				}
			}
			return i;
		}

		private static int FeedStationFromInventory(Inventory inventory, ZNetView nview, string prefab, int maxUnits, string rpcName, Func<int> remainingNow)
		{
			if (inventory == null || (Object)(object)nview == (Object)null || string.IsNullOrEmpty(prefab) || maxUnits <= 0)
			{
				return 0;
			}
			int i;
			for (i = 0; i < maxUnits; i++)
			{
				ItemData val = FindInventoryStack(inventory, prefab);
				if (val == null)
				{
					break;
				}
				bool cheated = val.m_cheated;
				string text = ((val.m_shared != null) ? val.m_shared.m_name : null);
				int num = CountInventory(inventory, prefab);
				inventory.RemoveItem(text, 1, -1, true);
				if (CountInventory(inventory, prefab) >= num)
				{
					Log.LogWarning((object)("CraftFromDrawersAndChests: the station feed could not remove '" + prefab + "' from the inventory, so it was left where it was and the feed stopped rather than repeating a removal that is not taking effect."));
					break;
				}
				int num2 = remainingNow?.Invoke() ?? (-1);
				nview.InvokeRPC(rpcName, StationRpcArgs(rpcName, prefab, cheated));
				if (num2 >= 0)
				{
					int num3 = remainingNow();
					if (num3 >= 0 && num3 >= num2)
					{
						Log.LogError((object)("CraftFromDrawersAndChests: the station did not take a fed '" + prefab + "' (its remaining capacity did not fall), so the feed has stopped after losing at most one unit rather than continuing to remove items that are not arriving."));
						break;
					}
				}
			}
			return i;
		}

		private static object[] StationRpcArgs(string rpcName, string prefab, bool cheated)
		{
			if (!string.Equals(rpcName, "RPC_AddOre", StringComparison.Ordinal))
			{
				return new object[0];
			}
			return new object[2] { prefab, cheated };
		}

		private static int CountInventory(Inventory inventory, string prefab)
		{
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val = allItems[i];
				if (val != null && !((Object)(object)val.m_dropPrefab == (Object)null) && string.Equals(((Object)val.m_dropPrefab).name, prefab, StringComparison.Ordinal))
				{
					num += val.m_stack;
				}
			}
			return num;
		}

		private static ItemData FindInventoryStack(Inventory inventory, string prefab)
		{
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null)
			{
				return null;
			}
			ItemData val = null;
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val2 = allItems[i];
				if (val2 != null && !((Object)(object)val2.m_dropPrefab == (Object)null) && string.Equals(((Object)val2.m_dropPrefab).name, prefab, StringComparison.Ordinal) && (val == null || val2.m_stack > val.m_stack))
				{
					val = val2;
				}
			}
			return val;
		}

		private static bool PullFromStorage(Player player, Vector3 origin, string prefab, ItemDrop item, FeatureFlags flags, int amount, bool fillUpTo)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (Object)(object)item == (Object)null || string.IsNullOrEmpty(prefab) || flags == null || amount <= 0)
			{
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			Requirement[] requirements = (Requirement[])(object)new Requirement[1]
			{
				new Requirement
				{
					m_amount = amount,
					m_resItem = item
				}
			};
			bool requested;
			bool flag = (fillUpTo ? PullEngine.PullToInventoryFill(requirements, 0, 1, inventory, Origin(origin), flags, allowUpgraderResources: false, out requested) : PullEngine.PullToInventory(requirements, 0, 1, inventory, Origin(origin), flags, allowUpgraderResources: false, out requested));
			if (flag && requested)
			{
				Debug(flags, "Station: pulled up to " + amount + "x '" + prefab + "' from storage into " + ((Object)player).name + "'s inventory.");
			}
			return flag && requested;
		}

		private static bool PullFirstInputs(Player player, Vector3 origin, List<ItemDrop> inputs, string disallowTypes, FeatureFlags flags, int amount, bool fillUpTo)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (amount <= 0)
			{
				return false;
			}
			for (int i = 0; i < inputs.Count; i++)
			{
				ItemDrop val = inputs[i];
				if (!((Object)(object)val == (Object)null))
				{
					string text = PrefabName(val);
					if (!string.IsNullOrEmpty(text) && !StationFeedRules.IsDisallowedType(disallowTypes, text) && PullFromStorage(player, origin, text, val, flags, amount, fillUpTo))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static List<ItemDrop> InputItems(List<ItemConversion> conversions)
		{
			List<ItemDrop> list = new List<ItemDrop>();
			if (conversions == null)
			{
				return list;
			}
			for (int i = 0; i < conversions.Count; i++)
			{
				list.Add(conversions[i]?.m_from);
			}
			return list;
		}

		private static List<ItemDrop> InputItems(List<ItemConversion> conversions)
		{
			List<ItemDrop> list = new List<ItemDrop>();
			if (conversions == null)
			{
				return list;
			}
			for (int i = 0; i < conversions.Count; i++)
			{
				list.Add(conversions[i]?.m_from);
			}
			return list;
		}

		[HarmonyPatch(typeof(Smelter), "OnHoverAddFuel")]
		[HarmonyPostfix]
		private static void SmelterOnHoverAddFuelPostfix(ref string __result)
		{
			AppendFillAllHint(ref __result, "$piece_smelter_add", PullSetting.Fuel);
		}

		[HarmonyPatch(typeof(Fireplace), "GetHoverText")]
		[HarmonyPostfix]
		private static void FireplaceGetHoverTextPostfix(Fireplace __instance, ref string __result)
		{
			AppendFillAllHint(ref __result, "$piece_smelter_add", PullSetting.Fuel);
		}

		[HarmonyPatch(typeof(Smelter), "OnHoverAddOre")]
		[HarmonyPostfix]
		private static void SmelterOnHoverAddOrePostfix(Smelter __instance, ref string __result)
		{
			AppendFillAllHint(ref __result, ((Object)(object)__instance != (Object)null) ? __instance.m_addOreTooltip : null, PullSetting.Ore);
		}

		private static void AppendFillAllHint(ref string result, string action, PullSetting setting)
		{
			FeatureFlags flags = PluginConfig.Flags;
			if (flags != null && flags.ModEnabled && PullSettingOn(flags, setting))
			{
				string text = StationFeedRules.FillAllHint(flags.FillAllModKey, action);
				if (text != null && Localization.instance != null)
				{
					result += Localization.instance.Localize(text);
				}
			}
		}

		private static FeatureFlags Enabled(PullSetting setting)
		{
			FeatureFlags flags = PluginConfig.Flags;
			if (flags == null || !flags.ModEnabled)
			{
				return null;
			}
			if (!PullKeyPatches.AllowByKey())
			{
				return null;
			}
			if (!PullSettingOn(flags, setting))
			{
				return null;
			}
			return flags;
		}

		private static bool PullSettingOn(FeatureFlags flags, PullSetting setting)
		{
			if (flags == null)
			{
				return false;
			}
			return setting switch
			{
				PullSetting.Fuel => flags.PullFuel, 
				PullSetting.Ore => flags.PullOre, 
				PullSetting.Cooking => flags.PullCooking, 
				_ => false, 
			};
		}

		private static bool FillAllHeld(FeatureFlags flags)
		{
			if (flags != null)
			{
				return PullKeyPatches.KeyHeld(flags.FillAllModKey);
			}
			return false;
		}

		private static string PrefabName(ItemDrop item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return null;
			}
			GameObject gameObject = ((Component)item).gameObject;
			if (!((Object)(object)gameObject != (Object)null))
			{
				return null;
			}
			return ((Object)gameObject).name;
		}

		private static string SharedName(ItemDrop item)
		{
			if (!((Object)(object)item != (Object)null) || item.m_itemData == null || item.m_itemData.m_shared == null)
			{
				return null;
			}
			return item.m_itemData.m_shared.m_name;
		}

		private static bool InventoryHasInput(Inventory inventory, List<ItemDrop> inputs)
		{
			if (inventory == null || inputs == null)
			{
				return false;
			}
			for (int i = 0; i < inputs.Count; i++)
			{
				string text = SharedName(inputs[i]);
				if (!string.IsNullOrEmpty(text) && inventory.GetItem(text, -1, false) != null)
				{
					return true;
				}
			}
			return