Decompiled source of StoreAndCraft v1.0.0

plugins/StoreAndCraft/StoreAndCraft.dll

Decompiled 6 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
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 HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("StoreAndCraft")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+90e867a18187e478d916d898790b505a6bfe1b78")]
[assembly: AssemblyProduct("StoreAndCraft")]
[assembly: AssemblyTitle("StoreAndCraft")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace StoreAndCraft;

public class ModConfig
{
	public const int ProtocolVersion = 1;

	public ConfigEntry<bool> LockConfig { get; }

	public ConfigEntry<bool> ModEnabled { get; }

	public ConfigEntry<bool> StoreEnabled { get; }

	public ConfigEntry<bool> CraftEnabled { get; }

	public ConfigEntry<bool> MustHaveExisting { get; }

	public ConfigEntry<bool> LeaveOneItem { get; }

	public ConfigEntry<bool> IgnoreHotbar { get; }

	public ConfigEntry<bool> HighlightOnStore { get; }

	public ConfigEntry<bool> PingOnStore { get; }

	public ConfigEntry<float> PlayerDumpRange { get; }

	public ConfigEntry<float> StoreRange { get; }

	public ConfigEntry<float> CraftRange { get; }

	public ConfigEntry<float> IntakeInterval { get; }

	public ConfigEntry<float> PauseSeconds { get; }

	public ConfigEntry<int> MaxTransfersPerTick { get; }

	public ConfigEntry<KeyboardShortcut> DumpKey { get; }

	public ConfigEntry<KeyboardShortcut> HoverStoreKey { get; }

	public ConfigEntry<KeyboardShortcut> PauseKey { get; }

	public ConfigEntry<KeyboardShortcut> SearchKey { get; }

	public ConfigEntry<KeyboardShortcut> PreventPullKey { get; }

	public ConfigEntry<KeyboardShortcut> RenameKey { get; }

	public ModConfig(ConfigFile file)
	{
		//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_022b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0252: Unknown result type (might be due to invalid IL or missing references)
		//IL_0282: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
		LockConfig = file.Bind<bool>("1 - General", "LockConfig", true, "If enabled, gameplay settings come from the server and clients cannot override them.");
		ModEnabled = file.Bind<bool>("1 - General", "ModEnabled", true, "Turns the whole mod on or off without uninstalling.");
		StoreEnabled = file.Bind<bool>("2 - Store", "StoreEnabled", true, "If enabled, ground items can be auto-stored and dump / middle-click store works.");
		CraftEnabled = file.Bind<bool>("3 - Craft", "CraftEnabled", true, "If enabled, crafting and building can use items stored in nearby chests.");
		MustHaveExisting = file.Bind<bool>("2 - Store", "MustHaveExisting", true, "If enabled, a chest only accepts an item if that item is already inside it. Empty chests will not vacuum new item types.");
		LeaveOneItem = file.Bind<bool>("3 - Craft", "LeaveOneItem", true, "If enabled, crafting/building leaves 1 item in each chest so auto-store can keep filling that stack.");
		IgnoreHotbar = file.Bind<bool>("2 - Store", "IgnoreHotbar", true, "If enabled, dump will not move items from the hotbar (first inventory row).");
		HighlightOnStore = file.Bind<bool>("2 - Store", "HighlightOnStore", true, "If enabled, a chest flashes when something is stored into it.");
		PingOnStore = file.Bind<bool>("2 - Store", "PingOnStore", false, "If enabled, a map ping is placed on the chest after a store.");
		PlayerDumpRange = file.Bind<float>("2 - Store", "PlayerDumpRange", 8f, "Fallback dump / middle-click range in meters (player to chest). YAML dumpRange overrides this.");
		StoreRange = file.Bind<float>("2 - Store", "StoreRange", 10f, "Fallback auto-store range in meters (item on ground to chest). YAML storeRange overrides this.");
		CraftRange = file.Bind<float>("3 - Craft", "CraftRange", 20f, "Fallback craft/build range in meters (player to chest). YAML craftRange overrides this.");
		IntakeInterval = file.Bind<float>("2 - Store", "IntakeInterval", 5f, "Seconds between automatic scans for ground items. Lower = snappier, higher = less CPU.");
		PauseSeconds = file.Bind<float>("2 - Store", "PauseSeconds", 10f, "How many seconds auto-store stays paused after the pause hotkey.");
		MaxTransfersPerTick = file.Bind<int>("1 - General", "MaxTransfersPerTick", 8, "Maximum item moves per frame. Raise only if storing feels too slow.");
		DumpKey = file.Bind<KeyboardShortcut>("4 - Keys", "DumpKey", new KeyboardShortcut((KeyCode)46, Array.Empty<KeyCode>()), "Hotkey: move allowed inventory stacks into nearby chests that already hold those items.");
		HoverStoreKey = file.Bind<KeyboardShortcut>("4 - Keys", "HoverStoreKey", new KeyboardShortcut((KeyCode)325, Array.Empty<KeyCode>()), "Hotkey: store the inventory item under the cursor. If no item is hovered, dumps all allowed stacks.");
		PauseKey = file.Bind<KeyboardShortcut>("4 - Keys", "PauseKey", new KeyboardShortcut((KeyCode)112, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Hotkey: pause auto-store for PauseSeconds.");
		SearchKey = file.Bind<KeyboardShortcut>("4 - Keys", "SearchKey", new KeyboardShortcut((KeyCode)121, Array.Empty<KeyCode>()), "Hold this key and click an inventory item to ping the nearest chest that contains it.");
		PreventPullKey = file.Bind<KeyboardShortcut>("4 - Keys", "PreventPullKey", new KeyboardShortcut((KeyCode)111, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Hotkey: locally disable pulling from chests for crafting/building.");
		RenameKey = file.Bind<KeyboardShortcut>("4 - Keys", "RenameKey", new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Look at a chest and hold this combo instead of opening it. Shift+E (Valheim alt-use) also renames. The custom name is shown on hover.");
	}

	public void WriteToPackage(ZPackage pkg)
	{
		pkg.Write(LockConfig.Value);
		pkg.Write(ModEnabled.Value);
		pkg.Write(StoreEnabled.Value);
		pkg.Write(CraftEnabled.Value);
		pkg.Write(MustHaveExisting.Value);
		pkg.Write(LeaveOneItem.Value);
		pkg.Write(PlayerDumpRange.Value);
		pkg.Write(StoreRange.Value);
		pkg.Write(CraftRange.Value);
		pkg.Write(IntakeInterval.Value);
		pkg.Write(PauseSeconds.Value);
		pkg.Write(MaxTransfersPerTick.Value);
	}

	public void ReadFromPackage(ZPackage pkg)
	{
		LockConfig.Value = pkg.ReadBool();
		ModEnabled.Value = pkg.ReadBool();
		StoreEnabled.Value = pkg.ReadBool();
		CraftEnabled.Value = pkg.ReadBool();
		MustHaveExisting.Value = pkg.ReadBool();
		LeaveOneItem.Value = pkg.ReadBool();
		PlayerDumpRange.Value = pkg.ReadSingle();
		StoreRange.Value = pkg.ReadSingle();
		CraftRange.Value = pkg.ReadSingle();
		IntakeInterval.Value = pkg.ReadSingle();
		PauseSeconds.Value = pkg.ReadSingle();
		MaxTransfersPerTick.Value = pkg.ReadInt();
	}
}
internal sealed class PieceRule
{
	public bool? Store;

	public bool? Craft;

	public float? StoreRange;

	public float? CraftRange;

	public float? DumpRange;

	public readonly List<string> Allow = new List<string>();

	public readonly List<string> Deny = new List<string>();
}
internal static class RulesFile
{
	private static readonly Dictionary<string, PieceRule> Pieces = new Dictionary<string, PieceRule>(StringComparer.OrdinalIgnoreCase);

	public static float? DefaultStoreRange { get; private set; }

	public static float? DefaultCraftRange { get; private set; }

	public static float? DefaultDumpRange { get; private set; }

	public static string Path => System.IO.Path.Combine(Paths.ConfigPath, "StoreAndCraft.rules.yml");

	public static void LoadOrCreate()
	{
		try
		{
			if (!File.Exists(Path))
			{
				File.WriteAllText(Path, DefaultText(), Encoding.UTF8);
			}
			Parse(File.ReadAllLines(Path));
			Plugin.Log.LogInfo((object)("StoreAndCraft rules loaded (" + Pieces.Count + " piece entries)."));
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("StoreAndCraft rules failed: " + ex.Message));
		}
	}

	public static PieceRule Get(string piecePrefab)
	{
		if (string.IsNullOrEmpty(piecePrefab))
		{
			return null;
		}
		if (!Pieces.TryGetValue(piecePrefab, out var value))
		{
			return null;
		}
		return value;
	}

	public static bool AllowsStore(string piecePrefab, ItemData item)
	{
		return Allows(piecePrefab, item, store: true);
	}

	public static bool AllowsCraft(string piecePrefab, string sharedName)
	{
		PieceRule pieceRule = Get(piecePrefab);
		if (pieceRule != null && pieceRule.Craft.HasValue && !pieceRule.Craft.Value)
		{
			return false;
		}
		if (pieceRule == null)
		{
			return true;
		}
		return MatchesLists(pieceRule, sharedName, null);
	}

	public static bool AllowsCraft(string piecePrefab, ItemData item)
	{
		return Allows(piecePrefab, item, store: false);
	}

	private static bool Allows(string piecePrefab, ItemData item, bool store)
	{
		PieceRule pieceRule = Get(piecePrefab);
		if (pieceRule == null)
		{
			return true;
		}
		if (store && pieceRule.Store.HasValue && !pieceRule.Store.Value)
		{
			return false;
		}
		if (!store && pieceRule.Craft.HasValue && !pieceRule.Craft.Value)
		{
			return false;
		}
		return MatchesLists(pieceRule, ItemIds.SharedName(item), item);
	}

	private static bool MatchesLists(PieceRule rule, string sharedName, ItemData item)
	{
		if (rule.Deny.Count > 0)
		{
			foreach (string item2 in rule.Deny)
			{
				if (item != null && ItemIds.Matches(item, item2))
				{
					return false;
				}
				if (item == null && NamesEqual(sharedName, item2))
				{
					return false;
				}
			}
		}
		if (rule.Allow.Count == 0)
		{
			return true;
		}
		foreach (string item3 in rule.Allow)
		{
			if (item != null && ItemIds.Matches(item, item3))
			{
				return true;
			}
			if (item == null && NamesEqual(sharedName, item3))
			{
				return true;
			}
		}
		return false;
	}

	private static bool NamesEqual(string sharedName, string token)
	{
		if (string.IsNullOrEmpty(sharedName) || string.IsNullOrEmpty(token))
		{
			return false;
		}
		if (string.Equals(sharedName, token, StringComparison.OrdinalIgnoreCase))
		{
			return true;
		}
		string b = ItemIds.SharedFromToken(token);
		return string.Equals(sharedName, b, StringComparison.OrdinalIgnoreCase);
	}

	public static float StoreRange(string piecePrefab, float fallback)
	{
		PieceRule pieceRule = Get(piecePrefab);
		if (pieceRule != null && pieceRule.StoreRange.HasValue)
		{
			return pieceRule.StoreRange.Value;
		}
		if (DefaultStoreRange.HasValue)
		{
			return DefaultStoreRange.Value;
		}
		return fallback;
	}

	public static float CraftRange(string piecePrefab, float fallback)
	{
		PieceRule pieceRule = Get(piecePrefab);
		if (pieceRule != null && pieceRule.CraftRange.HasValue)
		{
			return pieceRule.CraftRange.Value;
		}
		if (DefaultCraftRange.HasValue)
		{
			return DefaultCraftRange.Value;
		}
		return fallback;
	}

	public static float DumpRange(string piecePrefab, float fallback)
	{
		PieceRule pieceRule = Get(piecePrefab);
		if (pieceRule != null && pieceRule.DumpRange.HasValue)
		{
			return pieceRule.DumpRange.Value;
		}
		if (DefaultDumpRange.HasValue)
		{
			return DefaultDumpRange.Value;
		}
		return fallback;
	}

	public static float MaxScanRange(float cfgStore, float cfgCraft, float cfgDump)
	{
		float num = Mathf.Max(new float[3] { cfgStore, cfgCraft, cfgDump });
		if (DefaultStoreRange.HasValue)
		{
			num = Mathf.Max(num, DefaultStoreRange.Value);
		}
		if (DefaultCraftRange.HasValue)
		{
			num = Mathf.Max(num, DefaultCraftRange.Value);
		}
		if (DefaultDumpRange.HasValue)
		{
			num = Mathf.Max(num, DefaultDumpRange.Value);
		}
		foreach (PieceRule value in Pieces.Values)
		{
			if (value.StoreRange.HasValue)
			{
				num = Mathf.Max(num, value.StoreRange.Value);
			}
			if (value.CraftRange.HasValue)
			{
				num = Mathf.Max(num, value.CraftRange.Value);
			}
			if (value.DumpRange.HasValue)
			{
				num = Mathf.Max(num, value.DumpRange.Value);
			}
		}
		return num;
	}

	private static void Parse(string[] lines)
	{
		Pieces.Clear();
		DefaultStoreRange = null;
		DefaultCraftRange = null;
		DefaultDumpRange = null;
		string text = null;
		PieceRule pieceRule = null;
		string text2 = null;
		bool flag = false;
		for (int i = 0; i < lines.Length; i++)
		{
			string text3 = lines[i];
			int num = text3.IndexOf('#');
			if (num >= 0)
			{
				text3 = text3.Substring(0, num);
			}
			if (string.IsNullOrWhiteSpace(text3))
			{
				continue;
			}
			int j;
			for (j = 0; j < text3.Length && text3[j] == ' '; j++)
			{
			}
			text3 = text3.Trim();
			if (j == 0 && text3.EndsWith(":") && !text3.Equals("defaults:", StringComparison.OrdinalIgnoreCase) && !text3.Equals("pieces:", StringComparison.OrdinalIgnoreCase))
			{
				continue;
			}
			if (text3.Equals("defaults:", StringComparison.OrdinalIgnoreCase))
			{
				flag = true;
				text = null;
				pieceRule = null;
				text2 = null;
			}
			else if (text3.Equals("pieces:", StringComparison.OrdinalIgnoreCase))
			{
				flag = false;
				text = null;
				pieceRule = null;
				text2 = null;
			}
			else if (flag)
			{
				if (SplitKv(text3, out var key, out var value))
				{
					if (key.Equals("storeRange", StringComparison.OrdinalIgnoreCase))
					{
						DefaultStoreRange = ParseFloat(value);
					}
					else if (key.Equals("craftRange", StringComparison.OrdinalIgnoreCase))
					{
						DefaultCraftRange = ParseFloat(value);
					}
					else if (key.Equals("dumpRange", StringComparison.OrdinalIgnoreCase))
					{
						DefaultDumpRange = ParseFloat(value);
					}
				}
			}
			else if (j <= 2 && text3.EndsWith(":") && j < 4 && !IsKeyValue(text3))
			{
				text = text3.TrimEnd(new char[1] { ':' }).Trim();
				pieceRule = new PieceRule();
				Pieces[text] = pieceRule;
				text2 = null;
			}
			else
			{
				if (pieceRule == null)
				{
					continue;
				}
				if (text3.Equals("allow:", StringComparison.OrdinalIgnoreCase))
				{
					text2 = "allow";
					continue;
				}
				if (text3.Equals("deny:", StringComparison.OrdinalIgnoreCase))
				{
					text2 = "deny";
					continue;
				}
				if (text3.StartsWith("- "))
				{
					string item = text3.Substring(2).Trim().Trim(new char[1] { '"' });
					if (text2 == "allow")
					{
						pieceRule.Allow.Add(item);
					}
					else if (text2 == "deny")
					{
						pieceRule.Deny.Add(item);
					}
					continue;
				}
				text2 = null;
				if (SplitKv(text3, out var key2, out var value2))
				{
					if (key2.Equals("store", StringComparison.OrdinalIgnoreCase))
					{
						pieceRule.Store = ParseBool(value2);
					}
					else if (key2.Equals("craft", StringComparison.OrdinalIgnoreCase))
					{
						pieceRule.Craft = ParseBool(value2);
					}
					else if (key2.Equals("storeRange", StringComparison.OrdinalIgnoreCase))
					{
						pieceRule.StoreRange = ParseFloat(value2);
					}
					else if (key2.Equals("craftRange", StringComparison.OrdinalIgnoreCase))
					{
						pieceRule.CraftRange = ParseFloat(value2);
					}
					else if (key2.Equals("dumpRange", StringComparison.OrdinalIgnoreCase))
					{
						pieceRule.DumpRange = ParseFloat(value2);
					}
				}
			}
		}
	}

	private static bool IsKeyValue(string line)
	{
		int num = line.IndexOf(':');
		if (num > 0)
		{
			return num < line.Length - 1;
		}
		return false;
	}

	private static bool SplitKv(string line, out string key, out string value)
	{
		key = null;
		value = null;
		int num = line.IndexOf(':');
		if (num <= 0)
		{
			return false;
		}
		key = line.Substring(0, num).Trim();
		value = line.Substring(num + 1).Trim().Trim(new char[1] { '"' });
		return key.Length > 0;
	}

	private static bool ParseBool(string value)
	{
		if (!value.Equals("true", StringComparison.OrdinalIgnoreCase) && !value.Equals("yes", StringComparison.OrdinalIgnoreCase))
		{
			return value.Equals("on", StringComparison.OrdinalIgnoreCase);
		}
		return true;
	}

	private static float ParseFloat(string value)
	{
		if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
		{
			return result;
		}
		return 0f;
	}

	private static string DefaultText()
	{
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.AppendLine("# StoreAndCraft rules");
		stringBuilder.AppendLine("# File: BepInEx/config/StoreAndCraft.rules.yml");
		stringBuilder.AppendLine("# Distances are in meters. Saving this file on the server reloads and syncs.");
		stringBuilder.AppendLine("#");
		stringBuilder.AppendLine("# storeRange = how far a chest pulls items from the ground");
		stringBuilder.AppendLine("# dumpRange  = how far dump / middle-click can send items from the player to a chest");
		stringBuilder.AppendLine("# craftRange = how far crafting and building can pull materials from a chest");
		stringBuilder.AppendLine("# store/craft = true/false to allow storing or crafting from that piece");
		stringBuilder.AppendLine("# allow = only these items (prefab name like Wood, or $item_wood). Empty = all items.");
		stringBuilder.AppendLine("# deny  = never store/craft these items");
		stringBuilder.AppendLine();
		stringBuilder.AppendLine("defaults:");
		stringBuilder.AppendLine("  storeRange: 10");
		stringBuilder.AppendLine("  dumpRange: 12");
		stringBuilder.AppendLine("  craftRange: 20");
		stringBuilder.AppendLine();
		stringBuilder.AppendLine("pieces:");
		stringBuilder.AppendLine("  piece_chest:");
		stringBuilder.AppendLine("    store: true");
		stringBuilder.AppendLine("    craft: true");
		stringBuilder.AppendLine("  piece_chest_wood:");
		stringBuilder.AppendLine("    store: true");
		stringBuilder.AppendLine("    craft: true");
		stringBuilder.AppendLine("    # storeRange: 15");
		stringBuilder.AppendLine("    # dumpRange: 15");
		stringBuilder.AppendLine("    # craftRange: 25");
		return stringBuilder.ToString();
	}
}
internal static class RequirementBridge
{
	public static int CountNearby(Player player, string sharedName)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null || Plugin.Settings == null || string.IsNullOrEmpty(sharedName))
		{
			return 0;
		}
		return NearbyIndex.CountItem(((Component)player).transform.position, 0f, sharedName, Plugin.Settings.LeaveOneItem.Value);
	}

	public static bool SenderInRange(long sender, Vector3 target, float range)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)ZNet.instance == (Object)null)
		{
			return true;
		}
		ZNetPeer peer = ZNet.instance.GetPeer(sender);
		if (peer == null)
		{
			return true;
		}
		float num = 6f;
		return Vector3.Distance(peer.m_refPos, target) <= range + num;
	}
}
internal static class StagingPull
{
	public static bool PullingEnabled = true;

	private static float _retryCraftAt;

	private static bool _retryCraft;

	public static bool Active
	{
		get
		{
			if (Plugin.Settings != null && Plugin.Settings.ModEnabled.Value && Plugin.Settings.CraftEnabled.Value)
			{
				return PullingEnabled;
			}
			return false;
		}
	}

	public static void Tick()
	{
		if (_retryCraft && !(Time.time < _retryCraftAt))
		{
			_retryCraft = false;
			InventoryGui instance = InventoryGui.instance;
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)localPlayer == (Object)null) && !((Object)(object)Refs.CraftRecipe(instance) == (Object)null))
			{
				AccessToolsDoCraft(instance, localPlayer);
			}
		}
	}

	public static bool EnsureRecipe(Player player, Recipe recipe, int amount, out bool waiting)
	{
		waiting = false;
		if (!Active || (Object)(object)player == (Object)null || (Object)(object)recipe == (Object)null || recipe.m_resources == null)
		{
			return true;
		}
		return PullRequirements(player, recipe.m_resources, (!((Object)(object)recipe.m_item != (Object)null)) ? 1 : recipe.m_item.m_itemData.m_quality, -1, amount, out waiting);
	}

	public static bool EnsurePiece(Player player, Piece piece, out bool waiting)
	{
		waiting = false;
		if (!Active || (Object)(object)player == (Object)null || (Object)(object)piece == (Object)null || piece.m_resources == null)
		{
			return true;
		}
		return PullRequirements(player, piece.m_resources, 1, -1, 1, out waiting);
	}

	public static bool PullRequirements(Player player, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier, out bool waiting)
	{
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		waiting = false;
		if ((Object)(object)player == (Object)null || requirements == null)
		{
			return true;
		}
		Inventory inventory = ((Humanoid)player).GetInventory();
		if (inventory == null)
		{
			return true;
		}
		NearbyIndex.Tick();
		bool value = Plugin.Settings.LeaveOneItem.Value;
		float value2 = Plugin.Settings.CraftRange.Value;
		Vector3 position = ((Component)player).transform.position;
		bool result = true;
		foreach (Requirement val in requirements)
		{
			if (val == null || (Object)(object)val.m_resItem == (Object)null)
			{
				continue;
			}
			int num = val.GetAmount(qualityLevel) * Mathf.Max(1, multiplier);
			if (num <= 0)
			{
				continue;
			}
			string text = ((val.m_resItem.m_itemData != null) ? val.m_resItem.m_itemData.m_shared.m_name : null);
			if (string.IsNullOrEmpty(text))
			{
				continue;
			}
			int num2 = inventory.CountItems(text, itemQuality, true);
			int num3 = num - num2;
			if (num3 <= 0)
			{
				continue;
			}
			foreach (Container item in NearbyIndex.Current)
			{
				if (num3 <= 0)
				{
					break;
				}
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				string piecePrefab = ContainerFilter.PiecePrefab(item);
				if (!RulesFile.AllowsCraft(piecePrefab, text))
				{
					continue;
				}
				float num4 = RulesFile.CraftRange(piecePrefab, value2);
				if (!(ContainerFilter.Distance(position, ((Component)item).transform.position) > num4))
				{
					ZNetView val2 = Refs.View(item);
					if ((Object)(object)val2 == (Object)null || !val2.IsOwner())
					{
						TransferService.Withdraw(item, text, num3, inventory, value);
						result = false;
						waiting = true;
					}
					else
					{
						int num5 = TransferService.Withdraw(item, text, num3, inventory, value);
						num3 -= num5;
					}
				}
			}
		}
		return result;
	}

	public static void ScheduleCraftRetry()
	{
		_retryCraft = true;
		_retryCraftAt = Time.time + 0.2f;
	}

	private static void AccessToolsDoCraft(InventoryGui gui, Player player)
	{
		MethodInfo methodInfo = AccessTools.Method(typeof(InventoryGui), "DoCrafting", (Type[])null, (Type[])null);
		if (methodInfo != null)
		{
			methodInfo.Invoke(gui, new object[1] { player });
		}
	}
}
internal static class Hotkeys
{
	public static void Tick()
	{
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		if (Plugin.Settings == null || !Plugin.Settings.ModEnabled.Value || Console.IsVisible() || ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) || TextInput.IsVisible())
		{
			return;
		}
		if (KeyUtil.Down(Plugin.Settings.DumpKey.Value))
		{
			InventoryDump.DumpNearby();
		}
		if (KeyUtil.Down(Plugin.Settings.HoverStoreKey.Value))
		{
			HoverStore.TryStoreHovered();
		}
		if (KeyUtil.Down(Plugin.Settings.PauseKey.Value))
		{
			AutoIntake.TogglePause();
		}
		if (KeyUtil.Down(Plugin.Settings.RenameKey.Value))
		{
			ChestRename.TryOpen();
		}
		if (KeyUtil.Down(Plugin.Settings.PreventPullKey.Value))
		{
			StagingPull.PullingEnabled = !StagingPull.PullingEnabled;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				string text = (StagingPull.PullingEnabled ? Loc.T("on", "an") : Loc.T("off", "aus"));
				((Character)localPlayer).Message((MessageType)2, Loc.T("Chest pulling " + text + ".", "Truhen-Ziehen " + text + "."), 0, (Sprite)null, false);
			}
		}
	}

	public static bool SearchHeld()
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		if (Plugin.Settings != null)
		{
			return KeyUtil.Held(Plugin.Settings.SearchKey.Value);
		}
		return false;
	}
}
internal static class HoverStore
{
	public static ItemData GetHoveredPlayerItem()
	{
		InventoryGui instance = InventoryGui.instance;
		if ((Object)(object)instance == (Object)null || !InventoryGui.IsVisible() || (Object)(object)instance.m_playerGrid == (Object)null)
		{
			return null;
		}
		ItemData val = FromGrid(instance.m_playerGrid);
		if (val != null)
		{
			return val;
		}
		return FromGridByMouse(instance.m_playerGrid);
	}

	public static void TryStoreHovered()
	{
		if (Plugin.Settings == null || !Plugin.Settings.StoreEnabled.Value)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		if (!InventoryGui.IsVisible())
		{
			((Character)localPlayer).Message((MessageType)2, Loc.T("Open your inventory and middle-click an item.", "Inventar öffnen und mit mittlerer Maustaste auf ein Item klicken."), 0, (Sprite)null, false);
			return;
		}
		ItemData hoveredPlayerItem = GetHoveredPlayerItem();
		if (hoveredPlayerItem != null)
		{
			if (InventoryDump.StoreOne(hoveredPlayerItem))
			{
				((Character)localPlayer).Message((MessageType)1, Loc.T("Stored hovered item.", "Gehovertes Item eingelagert."), 0, (Sprite)null, false);
			}
		}
		else
		{
			InventoryDump.DumpNearby();
		}
	}

	private static ItemData FromGrid(InventoryGrid grid)
	{
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)grid == (Object)null)
		{
			return null;
		}
		MethodInfo methodInfo = AccessTools.Method(typeof(InventoryGrid), "GetHoveredElement", (Type[])null, (Type[])null);
		if (methodInfo == null)
		{
			return null;
		}
		object? obj = methodInfo.Invoke(grid, null);
		InventoryElement val = (InventoryElement)((obj is InventoryElement) ? obj : null);
		if ((Object)(object)val == (Object)null)
		{
			return null;
		}
		return grid.GetItem(val.Position);
	}

	private static ItemData FromGridByMouse(InventoryGrid grid)
	{
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)grid == (Object)null)
		{
			return null;
		}
		FieldInfo fieldInfo = AccessTools.Field(typeof(InventoryGrid), "m_elements");
		if (fieldInfo == null)
		{
			return null;
		}
		if (!(fieldInfo.GetValue(grid) is IList list))
		{
			return null;
		}
		Vector3 mousePosition = Input.mousePosition;
		foreach (object item2 in list)
		{
			InventoryElement val = (InventoryElement)((item2 is InventoryElement) ? item2 : null);
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			RectTransform elementRectTransform = val.GetElementRectTransform();
			if ((Object)(object)elementRectTransform == (Object)null)
			{
				continue;
			}
			Vector2 val2 = Vector2.op_Implicit(((Transform)elementRectTransform).InverseTransformPoint(mousePosition));
			Rect rect = elementRectTransform.rect;
			if (((Rect)(ref rect)).Contains(val2))
			{
				ItemData item = grid.GetItem(val.Position);
				if (item != null)
				{
					return item;
				}
			}
		}
		return null;
	}
}
internal static class KeyUtil
{
	public static bool Down(KeyboardShortcut shortcut)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0)
		{
			return false;
		}
		if (!MainDown(((KeyboardShortcut)(ref shortcut)).MainKey))
		{
			return false;
		}
		foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
		{
			if (!Held(modifier))
			{
				return false;
			}
		}
		return true;
	}

	public static bool Held(KeyboardShortcut shortcut)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0)
		{
			return false;
		}
		if (!MainHeld(((KeyboardShortcut)(ref shortcut)).MainKey))
		{
			return false;
		}
		return ModifiersHeld(shortcut);
	}

	public static bool ModifiersHeld(KeyboardShortcut shortcut)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		bool result = false;
		foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
		{
			result = true;
			if (!Held(modifier))
			{
				return false;
			}
		}
		return result;
	}

	public static string Format(KeyboardShortcut shortcut)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0)
		{
			return string.Empty;
		}
		List<string> list = new List<string>();
		foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
		{
			list.Add(Nice(modifier));
		}
		list.Add(Nice(((KeyboardShortcut)(ref shortcut)).MainKey));
		return string.Join("+", list.ToArray());
	}

	private unsafe static string Nice(KeyCode key)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Expected I4, but got Unknown
		switch (key - 303)
		{
		case 4:
		case 5:
			return "Alt";
		case 0:
		case 1:
			return "Shift";
		case 2:
		case 3:
			return "Ctrl";
		default:
			return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString();
		}
	}

	private static bool MainDown(KeyCode key)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		int num = MouseIndex(key);
		if (num >= 0)
		{
			return Input.GetMouseButtonDown(num);
		}
		return Input.GetKeyDown(key);
	}

	private static bool MainHeld(KeyCode key)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		int num = MouseIndex(key);
		if (num >= 0)
		{
			return Input.GetMouseButton(num);
		}
		return Input.GetKey(key);
	}

	private static bool Held(KeyCode key)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		int num = MouseIndex(key);
		if (num >= 0)
		{
			return Input.GetMouseButton(num);
		}
		return Input.GetKey(key);
	}

	private static int MouseIndex(KeyCode key)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Expected I4, but got Unknown
		return (key - 323) switch
		{
			0 => 0, 
			1 => 1, 
			2 => 2, 
			3 => 3, 
			4 => 4, 
			5 => 5, 
			6 => 6, 
			_ => -1, 
		};
	}
}
internal static class Loc
{
	public static string T(string en, string de)
	{
		return en;
	}
}
internal static class AdminUtil
{
	public static bool IsServer()
	{
		if ((Object)(object)ZNet.instance != (Object)null)
		{
			return ZNet.instance.IsServer();
		}
		return false;
	}

	public static bool CanEditSettings()
	{
		ZNet instance = ZNet.instance;
		if ((Object)(object)instance == (Object)null)
		{
			return true;
		}
		if (instance.IsDedicated())
		{
			return false;
		}
		if (instance.IsServer())
		{
			return true;
		}
		if (ConfigSync.ServerGrantedEdit)
		{
			return true;
		}
		return instance.LocalPlayerIsAdminOrHost();
	}
}
internal static class ConfigSync
{
	public const string RpcSyncName = "KAC_SyncConfig";

	public const string RpcRequestName = "KAC_RequestConfig";

	public const string HandshakeName = "KAC_Hello";

	public static bool IsApplyingRemoteConfig { get; private set; }

	public static bool Registered { get; private set; }

	public static bool HasReceivedConfig { get; private set; }

	public static bool ServerGrantedEdit { get; private set; }

	public static void Register()
	{
		if (ZRoutedRpc.instance != null)
		{
			if (!Registered)
			{
				ZRoutedRpc.instance.Register<ZPackage>("KAC_SyncConfig", (Action<long, ZPackage>)RPC_ReceiveConfig);
				ZRoutedRpc.instance.Register("KAC_RequestConfig", (Action<long>)RPC_RequestConfig);
				ZRoutedRpc.instance.Register<int, int>("KAC_Hello", (Action<long, int, int>)RPC_Hello);
				Registered = true;
				Plugin.Log.LogInfo((object)"StoreAndCraft RPCs registered.");
			}
			if (AdminUtil.IsServer())
			{
				HasReceivedConfig = true;
				ServerGrantedEdit = true;
				BroadcastConfig();
			}
			else if (!HasReceivedConfig)
			{
				RequestConfigFromServer();
			}
		}
	}

	public static void BroadcastConfig()
	{
		if (!AdminUtil.IsServer() || (Object)(object)ZNet.instance == (Object)null || Plugin.Settings == null)
		{
			return;
		}
		foreach (ZNetPeer peer in ZNet.instance.GetPeers())
		{
			if (peer != null && peer.IsReady())
			{
				SendToPeer(peer.m_uid);
			}
		}
	}

	public static void SendToPeer(long peerId)
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Expected O, but got Unknown
		if (AdminUtil.IsServer() && ZRoutedRpc.instance != null && Plugin.Settings != null)
		{
			bool flag = PeerIsAdmin(((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetPeer(peerId) : null);
			ZPackage val = new ZPackage();
			val.Write(ModConfigProtocol());
			val.Write(flag);
			Plugin.Settings.WriteToPackage(val);
			ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "KAC_SyncConfig", new object[1] { val });
		}
	}

	public static void RequestConfigFromServer()
	{
		if (!AdminUtil.IsServer() && ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "KAC_RequestConfig", Array.Empty<object>());
		}
	}

	private static int ModConfigProtocol()
	{
		return 1;
	}

	public static bool PeerIsAdmin(ZNetPeer peer)
	{
		if (peer?.m_rpc == null || (Object)(object)ZNet.instance == (Object)null)
		{
			return false;
		}
		try
		{
			ISocket socket = peer.m_rpc.GetSocket();
			if (socket == null)
			{
				return false;
			}
			string hostName = socket.GetHostName();
			return !string.IsNullOrEmpty(hostName) && ZNet.instance.IsAdmin(hostName);
		}
		catch
		{
			return false;
		}
	}

	private static void RPC_ReceiveConfig(long sender, ZPackage pkg)
	{
		if (AdminUtil.IsServer() || pkg == null || Plugin.Settings == null)
		{
			return;
		}
		int num = pkg.ReadInt();
		if (num != 1)
		{
			Plugin.Log.LogWarning((object)("StoreAndCraft config protocol mismatch: " + num));
			return;
		}
		ServerGrantedEdit = pkg.ReadBool();
		IsApplyingRemoteConfig = true;
		try
		{
			Plugin.Settings.ReadFromPackage(pkg);
			HasReceivedConfig = true;
		}
		finally
		{
			IsApplyingRemoteConfig = false;
		}
	}

	private static void RPC_RequestConfig(long sender)
	{
		if (AdminUtil.IsServer())
		{
			SendToPeer(sender);
		}
	}

	private static void RPC_Hello(long sender, int protocol, int major)
	{
		if (AdminUtil.IsServer())
		{
			VersionGate.Accept(sender, protocol, major);
			SendToPeer(sender);
		}
	}
}
internal class ConfigSyncRetry : MonoBehaviour
{
	private float _nextRequest;

	private int _attempts;

	private void Update()
	{
		if (AdminUtil.IsServer() || ConfigSync.HasReceivedConfig)
		{
			((Behaviour)this).enabled = false;
		}
		else if (ConfigSync.Registered && ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null) && !(Time.time < _nextRequest))
		{
			_attempts++;
			_nextRequest = Time.time + 2f;
			ConfigSync.RequestConfigFromServer();
			VersionGate.SendHello();
			if (_attempts == 1 || _attempts % 5 == 0)
			{
				Plugin.Log.LogInfo((object)("StoreAndCraft: waiting for server config (attempt " + _attempts + ")..."));
			}
		}
	}
}
internal static class ConfigWatch
{
	private static FileSystemWatcher _watcher;

	private static float _reloadAt;

	private static bool _pending;

	public static void Start()
	{
		if (_watcher != null)
		{
			return;
		}
		try
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath);
			_watcher.Filter = "StoreAndCraft.*";
			_watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite;
			_watcher.Changed += OnChanged;
			_watcher.Created += OnChanged;
			_watcher.EnableRaisingEvents = true;
			Plugin.Log.LogInfo((object)("StoreAndCraft config watcher on " + Paths.ConfigPath));
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("StoreAndCraft config watcher failed: " + ex.Message));
		}
	}

	public static void Tick()
	{
		if (_pending && !(Time.unscaledTime < _reloadAt))
		{
			_pending = false;
			Reload();
		}
	}

	private static void OnChanged(object sender, FileSystemEventArgs e)
	{
		_pending = true;
		_reloadAt = Time.unscaledTime + 0.4f;
	}

	private static void Reload()
	{
		try
		{
			if ((Object)(object)Plugin.Instance != (Object)null)
			{
				((BaseUnityPlugin)Plugin.Instance).Config.Reload();
			}
			RulesFile.LoadOrCreate();
			if (AdminUtil.IsServer())
			{
				ConfigSync.BroadcastConfig();
			}
			Plugin.Log.LogInfo((object)"StoreAndCraft config/rules reloaded from disk.");
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("StoreAndCraft reload failed: " + ex.Message));
		}
	}
}
internal static class TransferService
{
	private struct PendingMove
	{
		public Container Chest;

		public ItemDrop Drop;

		public ItemData Item;

		public Inventory From;

		public int Amount;

		public float Deadline;

		public bool Withdraw;

		public string SharedName;

		public bool LeaveOne;
	}

	public const string RpcRemove = "KAC_Remove";

	public const string RpcDeposit = "KAC_Deposit";

	public const string RpcGrant = "KAC_Grant";

	public const string RpcStoreDrop = "KAC_StoreDrop";

	private static readonly Queue<PendingMove> Queue = new Queue<PendingMove>();

	private static readonly Dictionary<int, float> ClaimThrottle = new Dictionary<int, float>();

	private static bool _grantRegistered;

	public static void RegisterGrant()
	{
		if (!_grantRegistered && ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.Register<ZPackage>("KAC_Grant", (Action<long, ZPackage>)RPC_Grant);
			_grantRegistered = true;
		}
	}

	public static void Tick()
	{
		int num = ((Plugin.Settings != null) ? Plugin.Settings.MaxTransfersPerTick.Value : 8);
		int num2 = 0;
		int count = Queue.Count;
		while (Queue.Count > 0 && num2 < num && count-- > 0)
		{
			PendingMove pendingMove = Queue.Dequeue();
			if (!(Time.time > pendingMove.Deadline) && !((Object)(object)pendingMove.Chest == (Object)null) && ContainerFilter.IsUsable(pendingMove.Chest))
			{
				if (!TryOwner(pendingMove.Chest, claim: true))
				{
					Queue.Enqueue(pendingMove);
					break;
				}
				Execute(pendingMove);
				num2++;
			}
		}
	}

	public static bool StoreItem(Container chest, Inventory from, ItemData item, int amount)
	{
		if ((Object)(object)chest == (Object)null || from == null || item == null || amount <= 0)
		{
			return false;
		}
		if (TryOwner(chest, claim: true))
		{
			return DepositLocal(chest, from, item, amount);
		}
		InvokeDeposit(chest, item, amount);
		Enqueue(new PendingMove
		{
			Chest = chest,
			From = from,
			Item = item,
			Amount = amount,
			Deadline = Time.time + 2f
		});
		return true;
	}

	public static bool StoreDrop(Container chest, ItemDrop drop)
	{
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)chest == (Object)null || (Object)(object)drop == (Object)null || drop.m_itemData == null)
		{
			return false;
		}
		ZNetView val = Refs.View(drop);
		if (TryOwner(chest, claim: true) && (Object)(object)val != (Object)null && val.IsOwner())
		{
			return StoreDropLocal(chest, drop);
		}
		if ((Object)(object)val != (Object)null && val.IsValid())
		{
			drop.RequestOwn();
		}
		ZNetView val2 = Refs.View(chest);
		if ((Object)(object)val2 != (Object)null && val2.IsValid() && (Object)(object)val != (Object)null && val.GetZDO() != null)
		{
			val2.InvokeRPC("KAC_StoreDrop", new object[1] { val.GetZDO().m_uid });
		}
		Enqueue(new PendingMove
		{
			Chest = chest,
			Drop = drop,
			Amount = drop.m_itemData.m_stack,
			Deadline = Time.time + 2.5f
		});
		return true;
	}

	public static int Withdraw(Container chest, string sharedName, int amount, Inventory playerInv, bool leaveOne)
	{
		if ((Object)(object)chest == (Object)null || playerInv == null || amount <= 0 || string.IsNullOrEmpty(sharedName))
		{
			return 0;
		}
		if (TryOwner(chest, claim: true))
		{
			return WithdrawLocal(chest, sharedName, amount, playerInv, leaveOne);
		}
		ZNetView val = Refs.View(chest);
		if ((Object)(object)val != (Object)null && val.IsValid())
		{
			val.InvokeRPC("KAC_Remove", new object[3]
			{
				sharedName,
				amount,
				leaveOne ? 1 : 0
			});
		}
		Enqueue(new PendingMove
		{
			Chest = chest,
			From = playerInv,
			SharedName = sharedName,
			Amount = amount,
			LeaveOne = leaveOne,
			Withdraw = true,
			Deadline = Time.time + 2f
		});
		return 0;
	}

	public static void RegisterOn(Container container)
	{
		ZNetView nv = Refs.View(container);
		if ((Object)(object)nv == (Object)null || !nv.IsValid())
		{
			return;
		}
		TryRegister(nv, "KAC_Remove", delegate
		{
			nv.Register<string, int, int>("KAC_Remove", (Action<long, string, int, int>)delegate(long sender, string name, int amount, int leaveOne)
			{
				OnRemove(container, sender, name, amount, leaveOne != 0);
			});
		});
		TryRegister(nv, "KAC_Deposit", delegate
		{
			nv.Register<ZPackage>("KAC_Deposit", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
			{
				OnDeposit(container, sender, pkg);
			});
		});
		TryRegister(nv, "KAC_StoreDrop", delegate
		{
			nv.Register<ZDOID>("KAC_StoreDrop", (Action<long, ZDOID>)delegate(long sender, ZDOID dropId)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				OnStoreDrop(container, sender, dropId);
			});
		});
	}

	private static void TryRegister(ZNetView nv, string name, Action register)
	{
		try
		{
			register();
		}
		catch (Exception ex)
		{
			Plugin.Log.LogDebug((object)("RPC register " + name + ": " + ex.Message));
		}
	}

	internal static void OnRemove(Container container, long sender, string sharedName, int amount, bool leaveOne)
	{
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Expected O, but got Unknown
		ZNetView val = Refs.View(container);
		if ((Object)(object)container == (Object)null || (Object)(object)val == (Object)null || !val.IsOwner() || amount <= 0 || !ValidateRpc(container, sender, (Plugin.Settings != null) ? Plugin.Settings.CraftRange.Value : 20f) || !RulesFile.AllowsCraft(ContainerFilter.PiecePrefab(container), sharedName))
		{
			return;
		}
		Inventory inventory = container.GetInventory();
		if (inventory != null)
		{
			int num = inventory.CountItems(sharedName, -1, true);
			if (leaveOne && num > 0)
			{
				num--;
			}
			int num2 = Mathf.Min(amount, num);
			if (num2 > 0)
			{
				inventory.RemoveItem(sharedName, num2, -1, true);
				ZPackage val2 = new ZPackage();
				val2.Write(sharedName);
				val2.Write(num2);
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "KAC_Grant", new object[1] { val2 });
			}
		}
	}

	internal static void OnDeposit(Container container, long sender, ZPackage pkg)
	{
		ZNetView val = Refs.View(container);
		if ((Object)(object)container == (Object)null || (Object)(object)val == (Object)null || !val.IsOwner() || pkg == null || !ValidateRpc(container, sender, MaxStoreRange()))
		{
			return;
		}
		string text = pkg.ReadString();
		int num = pkg.ReadInt();
		int num2 = pkg.ReadInt();
		int num3 = pkg.ReadInt();
		long num4 = pkg.ReadLong();
		string text2 = pkg.ReadString();
		if (num <= 0)
		{
			return;
		}
		Inventory inventory = container.GetInventory();
		if (inventory == null)
		{
			return;
		}
		ItemData val2 = inventory.AddItem(text, num, num2, num3, num4, text2, false, false);
		if (val2 != null)
		{
			if (!RulesFile.AllowsStore(ContainerFilter.PiecePrefab(container), val2))
			{
				inventory.RemoveItem(val2, val2.m_stack);
			}
			else
			{
				Highlight(container);
			}
		}
	}

	internal static void OnStoreDrop(Container container, long sender, ZDOID dropId)
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		ZNetView val = Refs.View(container);
		if ((Object)(object)container == (Object)null || (Object)(object)val == (Object)null || !val.IsOwner() || (Object)(object)ZNetScene.instance == (Object)null || !ValidateRpc(container, sender, MaxStoreRange()))
		{
			return;
		}
		GameObject val2 = ZNetScene.instance.FindInstance(dropId);
		if ((Object)(object)val2 == (Object)null)
		{
			return;
		}
		ItemDrop component = val2.GetComponent<ItemDrop>();
		if (!((Object)(object)component == (Object)null) && component.m_itemData != null && RulesFile.AllowsStore(ContainerFilter.PiecePrefab(container), component.m_itemData) && !(Vector3.Distance(((Component)component).transform.position, ((Component)container).transform.position) > MaxStoreRange() + 6f))
		{
			ZNetView val3 = Refs.View(component);
			if ((Object)(object)val3 != (Object)null && !val3.IsOwner())
			{
				component.RequestOwn();
			}
			else
			{
				StoreDropLocal(container, component);
			}
		}
	}

	private static void RPC_Grant(long sender, ZPackage pkg)
	{
		Player localPlayer = Player.m_localPlayer;
		if (!((Object)(object)localPlayer == (Object)null) && pkg != null)
		{
			string text = pkg.ReadString();
			int num = pkg.ReadInt();
			GameObject val = ItemIds.PrefabFromToken(text);
			if ((Object)(object)val == (Object)null)
			{
				val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(text) : null);
			}
			if (!((Object)(object)val == (Object)null))
			{
				((Humanoid)localPlayer).GetInventory().AddItem(val, num);
			}
		}
	}

	private static bool Execute(PendingMove op)
	{
		if ((Object)(object)op.Drop != (Object)null)
		{
			ZNetView val = Refs.View(op.Drop);
			if ((Object)(object)val != (Object)null && !val.IsOwner())
			{
				op.Drop.RequestOwn();
				Queue.Enqueue(op);
				return false;
			}
			return StoreDropLocal(op.Chest, op.Drop);
		}
		if (op.Withdraw)
		{
			return WithdrawLocal(op.Chest, op.SharedName, op.Amount, op.From, op.LeaveOne) > 0;
		}
		return DepositLocal(op.Chest, op.From, op.Item, op.Amount);
	}

	private static bool DepositLocal(Container chest, Inventory from, ItemData item, int amount)
	{
		Inventory inventory = chest.GetInventory();
		if (inventory == null || item == null)
		{
			return false;
		}
		int num = Mathf.Min(amount, item.m_stack);
		if (num <= 0 || !inventory.CanAddItem(item, num))
		{
			return false;
		}
		ItemData val = item.Clone();
		val.m_stack = num;
		if (!inventory.AddItem(val))
		{
			return false;
		}
		from.RemoveItem(item, num);
		Highlight(chest);
		return true;
	}

	private static bool StoreDropLocal(Container chest, ItemDrop drop)
	{
		if ((Object)(object)drop == (Object)null || drop.m_itemData == null)
		{
			return false;
		}
		Inventory inventory = chest.GetInventory();
		if (inventory == null || !inventory.CanAddItem(drop.m_itemData, drop.m_itemData.m_stack))
		{
			return false;
		}
		ItemData val = drop.m_itemData.Clone();
		if (!inventory.AddItem(val))
		{
			return false;
		}
		ZNetView val2 = Refs.View(drop);
		if ((Object)(object)val2 != (Object)null && val2.IsValid() && (Object)(object)ZNetScene.instance != (Object)null)
		{
			ZNetScene.instance.Destroy(((Component)drop).gameObject);
		}
		else
		{
			Object.Destroy((Object)(object)((Component)drop).gameObject);
		}
		Highlight(chest);
		return true;
	}

	private static int WithdrawLocal(Container chest, string sharedName, int amount, Inventory playerInv, bool leaveOne)
	{
		Inventory inventory = chest.GetInventory();
		if (inventory == null || playerInv == null)
		{
			return 0;
		}
		int num = inventory.CountItems(sharedName, -1, true);
		if (leaveOne && num > 0)
		{
			num--;
		}
		int num2 = Mathf.Min(amount, num);
		if (num2 <= 0)
		{
			return 0;
		}
		GameObject val = ItemIds.PrefabFromToken(sharedName);
		if ((Object)(object)val == (Object)null && (Object)(object)ObjectDB.instance != (Object)null)
		{
			val = ObjectDB.instance.GetItemPrefab(sharedName);
		}
		if ((Object)(object)val == (Object)null)
		{
			return 0;
		}
		if (!playerInv.CanAddItem(val, num2))
		{
			while (num2 > 0 && !playerInv.CanAddItem(val, num2))
			{
				num2--;
			}
			if (num2 <= 0)
			{
				return 0;
			}
		}
		if (!playerInv.AddItem(val, num2))
		{
			return 0;
		}
		inventory.RemoveItem(sharedName, num2, -1, true);
		return num2;
	}

	private static float MaxStoreRange()
	{
		if (Plugin.Settings == null)
		{
			return 12f;
		}
		return RulesFile.MaxScanRange(Plugin.Settings.StoreRange.Value, Plugin.Settings.CraftRange.Value, Plugin.Settings.PlayerDumpRange.Value);
	}

	private static bool ValidateRpc(Container container, long sender, float range)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		if (!ContainerFilter.IsUsable(container))
		{
			return false;
		}
		if (!PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, true))
		{
			return false;
		}
		if (!RequirementBridge.SenderInRange(sender, ((Component)container).transform.position, range))
		{
			return false;
		}
		return true;
	}

	private static bool TryOwner(Container chest, bool claim)
	{
		ZNetView val = Refs.View(chest);
		if ((Object)(object)val == (Object)null || !val.IsValid())
		{
			return false;
		}
		if (val.IsOwner())
		{
			return true;
		}
		if (!claim)
		{
			return false;
		}
		int instanceID = ((Object)chest).GetInstanceID();
		if (ClaimThrottle.TryGetValue(instanceID, out var value) && Time.time < value)
		{
			return false;
		}
		val.ClaimOwnership();
		ClaimThrottle[instanceID] = Time.time + 0.35f;
		return val.IsOwner();
	}

	private static void InvokeDeposit(Container chest, ItemData item, int amount)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		ZNetView val = Refs.View(chest);
		if (!((Object)(object)val == (Object)null) && val.IsValid() && item != null)
		{
			ZPackage val2 = new ZPackage();
			val2.Write(ItemIds.PrefabName(item) ?? ItemIds.SharedName(item) ?? "");
			val2.Write(amount);
			val2.Write(item.m_quality);
			val2.Write(item.m_variant);
			val2.Write(item.m_crafterID);
			val2.Write(item.m_crafterName ?? "");
			val.InvokeRPC("KAC_Deposit", new object[1] { val2 });
		}
	}

	private static void Enqueue(PendingMove op)
	{
		if (Queue.Count <= 80)
		{
			Queue.Enqueue(op);
		}
	}

	public static void Highlight(Container chest)
	{
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)chest == (Object)null || Plugin.Settings == null)
		{
			return;
		}
		if (Plugin.Settings.HighlightOnStore.Value)
		{
			WearNTear component = ((Component)chest).GetComponent<WearNTear>();
			if ((Object)(object)component != (Object)null)
			{
				component.Highlight();
			}
		}
		if (Plugin.Settings.PingOnStore.Value && (Object)(object)Chat.instance != (Object)null)
		{
			Chat.instance.SendPing(((Component)chest).transform.position);
		}
	}
}
internal class VersionGate : MonoBehaviour
{
	public const int Major = 1;

	public const float GraceSeconds = 12f;

	private static readonly Dictionary<long, float> Deadline = new Dictionary<long, float>();

	private static readonly HashSet<long> Verified = new HashSet<long>();

	public static void OnPeerReady(long peerId)
	{
		if (AdminUtil.IsServer() && peerId != 0L && !Verified.Contains(peerId) && !Deadline.ContainsKey(peerId))
		{
			Deadline[peerId] = Time.time + 12f;
		}
	}

	public static void Accept(long peerId, int protocol, int major)
	{
		if (AdminUtil.IsServer())
		{
			if (protocol != 1 || major != 1)
			{
				Plugin.Log.LogWarning((object)("StoreAndCraft: rejecting peer " + peerId + " (protocol " + protocol + "/" + 1 + ", major " + major + "/" + 1 + ")."));
				Kick(peerId);
			}
			else
			{
				Verified.Add(peerId);
				Deadline.Remove(peerId);
			}
		}
	}

	public static void SendHello()
	{
		if (!AdminUtil.IsServer() && ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "KAC_Hello", new object[2] { 1, 1 });
		}
	}

	private void Update()
	{
		if (!AdminUtil.IsServer() || (Object)(object)ZNet.instance == (Object)null || Deadline.Count == 0)
		{
			return;
		}
		List<long> list = new List<long>();
		foreach (KeyValuePair<long, float> item in Deadline)
		{
			if (!Verified.Contains(item.Key) && Time.time >= item.Value)
			{
				list.Add(item.Key);
			}
		}
		foreach (long item2 in list)
		{
			Deadline.Remove(item2);
			Plugin.Log.LogWarning((object)("StoreAndCraft: kicking peer " + item2 + " (missing StoreAndCraft or handshake timeout)."));
			Kick(item2);
		}
	}

	private static void Kick(long peerId)
	{
		if (!((Object)(object)ZNet.instance == (Object)null))
		{
			ZNetPeer peer = ZNet.instance.GetPeer(peerId);
			if (peer != null)
			{
				ZNet.instance.Disconnect(peer);
			}
		}
	}
}
[HarmonyPatch(typeof(Container), "Interact")]
internal static class ContainerInteractRenamePatch
{
	private static bool Prefix(Container __instance, Humanoid character, bool hold, bool alt, ref bool __result)
	{
		if (hold || (Object)(object)character != (Object)(object)Player.m_localPlayer)
		{
			return true;
		}
		if (!ChestRename.WantsRename(alt))
		{
			return true;
		}
		if (!ChestRename.TryOpen(__instance, warnIfMissing: false))
		{
			return true;
		}
		__result = true;
		return false;
	}
}
[HarmonyPatch(typeof(Container), "GetHoverName")]
internal static class ContainerHoverNamePatch
{
	private static void Postfix(Container __instance, ref string __result)
	{
		string text = ChestNames.Get(__instance);
		if (!string.IsNullOrEmpty(text))
		{
			__result = text;
		}
	}
}
[HarmonyPatch(typeof(Container), "GetHoverText")]
internal static class ContainerHoverTextPatch
{
	private static void Postfix(Container __instance, ref string __result)
	{
		if (string.IsNullOrEmpty(__result))
		{
			return;
		}
		string text = ChestNames.Get(__instance);
		if (!string.IsNullOrEmpty(text))
		{
			string text2 = Refs.VanillaHoverName(__instance);
			if (!string.IsNullOrEmpty(text2) && Localization.instance != null)
			{
				text2 = Localization.instance.Localize(text2);
			}
			if (!string.IsNullOrEmpty(text2) && __result.StartsWith(text2))
			{
				__result = text + __result.Substring(text2.Length);
			}
			else if (__result.IndexOf(text, StringComparison.Ordinal) < 0)
			{
				__result = text + "\n" + __result;
			}
		}
		__result = __result + "\n[<color=yellow><b>" + ChestRename.PromptLabel() + "</b></color>] Rename";
	}
}
[HarmonyPatch(typeof(Inventory))]
internal static class InventoryCountPatches
{
	internal static int Skip;

	[HarmonyPostfix]
	[HarmonyPatch("CountItems")]
	private static void CountItemsPostfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result)
	{
		if (Skip > 0 || !StagingPull.Active)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || ((Humanoid)localPlayer).GetInventory() != __instance || string.IsNullOrEmpty(name))
		{
			return;
		}
		Skip++;
		try
		{
			__result += RequirementBridge.CountNearby(localPlayer, name);
		}
		finally
		{
			Skip--;
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch("HaveItem", new Type[]
	{
		typeof(string),
		typeof(bool)
	})]
	private static void HaveItemPostfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result)
	{
		if (__result || Skip > 0 || !StagingPull.Active)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || ((Humanoid)localPlayer).GetInventory() != __instance)
		{
			return;
		}
		Skip++;
		try
		{
			__result = RequirementBridge.CountNearby(localPlayer, name) > 0;
		}
		finally
		{
			Skip--;
		}
	}
}
[HarmonyPatch(typeof(Container), "Awake")]
internal static class ContainerAwakePatch
{
	private static void Postfix(Container __instance)
	{
		TransferService.RegisterOn(__instance);
		if ((Object)(object)((Component)__instance).GetComponent<ChestRenameReceiver>() == (Object)null)
		{
			((Component)__instance).gameObject.AddComponent<ChestRenameReceiver>();
		}
	}
}
[HarmonyPatch(typeof(Game), "Start")]
internal static class GameStartPatch
{
	private static void Postfix(Game __instance)
	{
		ConfigSync.Register();
		TransferService.RegisterGrant();
		VersionGate.SendHello();
		if ((Object)(object)__instance != (Object)null && (Object)(object)((Component)__instance).GetComponent<ConfigSyncRetry>() == (Object)null)
		{
			((Component)__instance).gameObject.AddComponent<ConfigSyncRetry>();
		}
		if ((Object)(object)__instance != (Object)null && (Object)(object)((Component)__instance).GetComponent<VersionGate>() == (Object)null)
		{
			((Component)__instance).gameObject.AddComponent<VersionGate>();
		}
	}
}
[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
internal static class PeerInfoPatch
{
	private static void Postfix(ZRpc rpc)
	{
		if (!AdminUtil.IsServer() || rpc == null || (Object)(object)ZNet.instance == (Object)null)
		{
			return;
		}
		foreach (ZNetPeer peer in ZNet.instance.GetPeers())
		{
			if (peer != null && peer.m_rpc == rpc)
			{
				VersionGate.OnPeerReady(peer.m_uid);
				ConfigSync.SendToPeer(peer.m_uid);
				break;
			}
		}
	}
}
[HarmonyPatch(typeof(Player), "OnSpawned")]
internal static class PlayerSpawnedPatch
{
	private static void Postfix(Player __instance)
	{
		if (!((Object)(object)__instance == (Object)null) && ((Character)__instance).IsOwner() && !AdminUtil.IsServer())
		{
			VersionGate.SendHello();
			if (!ConfigSync.HasReceivedConfig)
			{
				ConfigSync.RequestConfigFromServer();
			}
		}
	}
}
[HarmonyPatch(typeof(Terminal), "InitTerminal")]
internal static class TerminalInitPatch
{
	private static bool _added;

	private static void Postfix()
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Expected O, but got Unknown
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		if (!_added)
		{
			_added = true;
			new ConsoleCommand("storesearch", "Find an item in nearby containers", new ConsoleEvent(SearchPing.OnCommand), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}
	}
}
[HarmonyPatch]
internal static class PlayerCraftPatches
{
	[HarmonyPrefix]
	[HarmonyPatch(typeof(Player), "ConsumeResources")]
	private static void ConsumeResourcesPrefix(Player __instance, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier)
	{
		if (!((Object)(object)__instance == (Object)null) && ((Character)__instance).IsOwner() && StagingPull.Active)
		{
			StagingPull.PullRequirements(__instance, requirements, qualityLevel, itemQuality, multiplier, out var _);
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	private static bool DoCraftingPrefix(InventoryGui __instance, Player player)
	{
		Recipe val = Refs.CraftRecipe(__instance);
		if (!StagingPull.Active || (Object)(object)player == (Object)null || (Object)(object)__instance == (Object)null || (Object)(object)val == (Object)null)
		{
			return true;
		}
		int amount = ((!Refs.MultiCrafting(__instance)) ? 1 : Mathf.Max(1, __instance.m_multiCraftAmount));
		StagingPull.EnsureRecipe(player, val, amount, out var waiting);
		if (waiting)
		{
			StagingPull.ScheduleCraftRetry();
			return false;
		}
		return true;
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	private static bool TryPlacePiecePrefix(Player __instance, Piece piece, ref bool __result)
	{
		if (!StagingPull.Active || (Object)(object)__instance == (Object)null || (Object)(object)piece == (Object)null)
		{
			return true;
		}
		StagingPull.EnsurePiece(__instance, piece, out var waiting);
		if (waiting)
		{
			__result = false;
			return false;
		}
		return true;
	}
}
[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
internal static class CraftUiHintPatch
{
	private static readonly Color Flash = new Color(1f, 0.92f, 0.2f, 1f);

	private static void Postfix(Transform elementRoot, Requirement req, Player player, bool craft, int quality, int craftMultiplier, bool __result)
	{
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		if (!__result || !StagingPull.Active || (Object)(object)player == (Object)null || req == null || (Object)(object)req.m_resItem == (Object)null || (Object)(object)elementRoot == (Object)null)
		{
			return;
		}
		string text = ((req.m_resItem.m_itemData != null) ? req.m_resItem.m_itemData.m_shared.m_name : null);
		if (string.IsNullOrEmpty(text))
		{
			return;
		}
		InventoryCountPatches.Skip++;
		try
		{
			((Humanoid)player).GetInventory().CountItems(text, -1, true);
		}
		finally
		{
			InventoryCountPatches.Skip--;
		}
		if (RequirementBridge.CountNearby(player, text) <= 0)
		{
			return;
		}
		TMP_Text[] componentsInChildren = ((Component)elementRoot).GetComponentsInChildren<TMP_Text>(true);
		foreach (TMP_Text val in componentsInChildren)
		{
			if (!((Object)(object)val == (Object)null))
			{
				((Graphic)val).color = Color.Lerp(((Graphic)val).color, Flash, 0.65f);
			}
		}
	}
}
[HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")]
internal static class SearchClickPatch
{
	private static bool Prefix(ItemData item)
	{
		if (item == null || !Hotkeys.SearchHeld())
		{
			return true;
		}
		SearchPing.PingItem(item);
		return false;
	}
}
[BepInPlugin("com.morda.storeandcraft", "StoreAndCraft", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
	public const string ModGuid = "com.morda.storeandcraft";

	public const string ModName = "StoreAndCraft";

	public const string ModVersion = "1.0.0";

	public const string ModAuthor = "Morda";

	private Harmony _harmony;

	internal static Plugin Instance { get; private set; }

	internal static ManualLogSource Log { get; private set; }

	internal static ModConfig Settings { get; private set; }

	private void Awake()
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		Settings = new ModConfig(((BaseUnityPlugin)this).Config);
		RulesFile.LoadOrCreate();
		ConfigWatch.Start();
		_harmony = new Harmony("com.morda.storeandcraft");
		_harmony.PatchAll(Assembly.GetExecutingAssembly());
		((BaseUnityPlugin)this).Logger.LogInfo((object)"StoreAndCraft v1.0.0 by Morda loaded.");
	}

	private void Update()
	{
		ConfigWatch.Tick();
		TransferService.Tick();
		if (!((Object)(object)Player.m_localPlayer == (Object)null))
		{
			NearbyIndex.Tick();
			AutoIntake.Tick();
			StagingPull.Tick();
		}
	}

	private void LateUpdate()
	{
		if (!((Object)(object)Player.m_localPlayer == (Object)null))
		{
			Hotkeys.Tick();
		}
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}
}
internal static class Refs
{
	private static readonly FieldInfo ItemDropNview = AccessTools.Field(typeof(ItemDrop), "m_nview");

	private static readonly FieldInfo ItemDropInstances = AccessTools.Field(typeof(ItemDrop), "s_instances");

	private static readonly FieldInfo ContainerNview = AccessTools.Field(typeof(Container), "m_nview");

	private static readonly FieldInfo ContainerName = AccessTools.Field(typeof(Container), "m_name");

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

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

	public static ZNetView View(Container container)
	{
		if ((Object)(object)container == (Object)null)
		{
			return null;
		}
		if (ContainerNview != null)
		{
			object? value = ContainerNview.GetValue(container);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
		}
		return ((Component)container).GetComponent<ZNetView>();
	}

	public static string VanillaHoverName(Container container)
	{
		if ((Object)(object)container == (Object)null || ContainerName == null)
		{
			return null;
		}
		return ContainerName.GetValue(container) as string;
	}

	public static ZNetView View(ItemDrop drop)
	{
		if ((Object)(object)drop == (Object)null)
		{
			return null;
		}
		if (ItemDropNview != null)
		{
			object? value = ItemDropNview.GetValue(drop);
			ZNetView val = (ZNetView)((value is ZNetView) ? value : null);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
		}
		return ((Component)drop).GetComponent<ZNetView>();
	}

	public static List<ItemDrop> Drops()
	{
		if (ItemDropInstances == null)
		{
			return null;
		}
		return ItemDropInstances.GetValue(null) as List<ItemDrop>;
	}

	public static Recipe CraftRecipe(InventoryGui gui)
	{
		if (!((Object)(object)gui != (Object)null) || !(GuiRecipe != null))
		{
			return null;
		}
		object? value = GuiRecipe.GetValue(gui);
		return (Recipe)((value is Recipe) ? value : null);
	}

	public static bool MultiCrafting(InventoryGui gui)
	{
		if ((Object)(object)gui == (Object)null || GuiMulti == null)
		{
			return false;
		}
		object value = GuiMulti.GetValue(gui);
		if (value is bool)
		{
			return (bool)value;
		}
		return false;
	}
}
internal static class AutoIntake
{
	private static float _next;

	public static float PauseUntil { get; set; }

	public static bool Paused => Time.time < PauseUntil;

	public static void TogglePause()
	{
		if (Plugin.Settings != null)
		{
			if (Paused)
			{
				PauseUntil = 0f;
				Tell(Loc.T("Auto-store resumed.", "Auto-Einlagern wieder an."));
			}
			else
			{
				PauseUntil = Time.time + Plugin.Settings.PauseSeconds.Value;
				Tell(Loc.T("Auto-store paused for " + Plugin.Settings.PauseSeconds.Value + "s.", "Auto-Einlagern pausiert für " + Plugin.Settings.PauseSeconds.Value + "s."));
			}
		}
	}

	public static void Tick()
	{
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_0184: Unknown result type (might be due to invalid IL or missing references)
		//IL_0196: Unknown result type (might be due to invalid IL or missing references)
		//IL_019f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
		if (Plugin.Settings == null || !Plugin.Settings.ModEnabled.Value || !Plugin.Settings.StoreEnabled.Value || Paused)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || Time.time < _next)
		{
			return;
		}
		_next = Time.time + Mathf.Max(1f, Plugin.Settings.IntakeInterval.Value);
		List<ItemDrop> list = Refs.Drops();
		if (list == null)
		{
			return;
		}
		int num = 0;
		int value = Plugin.Settings.MaxTransfersPerTick.Value;
		Vector3 position = ((Component)localPlayer).transform.position;
		foreach (ItemDrop item in list)
		{
			if (num >= value)
			{
				break;
			}
			if ((Object)(object)item == (Object)null || item.m_itemData == null)
			{
				continue;
			}
			ZNetView val = Refs.View(item);
			if ((Object)(object)val == (Object)null || !val.IsValid() || item.IsPiece() || !item.CanPickup(true))
			{
				continue;
			}
			float value2 = Plugin.Settings.StoreRange.Value;
			Container val2 = null;
			float num2 = float.MaxValue;
			foreach (Container item2 in NearbyIndex.Current)
			{
				if (!((Object)(object)item2 == (Object)null))
				{
					float num3 = RulesFile.StoreRange(ContainerFilter.PiecePrefab(item2), value2);
					float num4 = ContainerFilter.Distance(((Component)item).transform.position, ((Component)item2).transform.position);
					if (!(num4 > num3) && !(ContainerFilter.Distance(position, ((Component)item2).transform.position) > NearbyIndex.ScanRange()) && ChestPicker.CanAccept(item2, item.m_itemData, ((Component)item).transform.position, Plugin.Settings.MustHaveExisting.Value) && num4 < num2)
					{
						num2 = num4;
						val2 = item2;
					}
				}
			}
			if (!((Object)(object)val2 == (Object)null) && TransferService.StoreDrop(val2, item))
			{
				num++;
			}
		}
	}

	private static void Tell(string msg)
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer != (Object)null)
		{
			((Character)localPlayer).Message((MessageType)2, msg, 0, (Sprite)null, false);
		}
	}
}
internal static class ChestNames
{
	public const string ZdoKey = "kac_label";

	public const int MaxLength = 32;

	public static bool CanRename(Container container)
	{
		ZNetView val = Refs.View(container);
		if ((Object)(object)val != (Object)null && val.IsValid())
		{
			return val.GetZDO() != null;
		}
		return false;
	}

	public static string Get(Container container)
	{
		ZNetView val = Refs.View(container);
		if ((Object)(object)val == (Object)null || !val.IsValid() || val.GetZDO() == null)
		{
			return null;
		}
		string text = val.GetZDO().GetString("kac_label", string.Empty);
		if (!string.IsNullOrEmpty(text))
		{
			return text;
		}
		return null;
	}

	public static bool Set(Container container, string name)
	{
		ZNetView val = Refs.View(container);
		if ((Object)(object)val == (Object)null || !val.IsValid() || val.GetZDO() == null)
		{
			return false;
		}
		if (!val.IsOwner())
		{
			val.ClaimOwnership();
		}
		val.GetZDO().Set("kac_label", Sanitize(name));
		return true;
	}

	public static string Sanitize(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return string.Empty;
		}
		name = name.Trim();
		if (name.Length > 32)
		{
			name = name.Substring(0, 32);
		}
		return name;
	}

	public static Container Hovered()
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return null;
		}
		GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject();
		Container val = (((Object)(object)hoverObject != (Object)null) ? hoverObject.GetComponentInParent<Container>() : null);
		if ((Object)(object)val != (Object)null)
		{
			return val;
		}
		Piece hoveringPiece = localPlayer.GetHoveringPiece();
		if (!((Object)(object)hoveringPiece != (Object)null))
		{
			return null;
		}
		return ((Component)hoveringPiece).GetComponent<Container>();
	}
}
internal class ChestRenameReceiver : MonoBehaviour, TextReceiver
{
	public string GetText()
	{
		return ChestNames.Get(((Component)this).GetComponent<Container>()) ?? string.Empty;
	}

	public void SetText(string text)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		Container component = ((Component)this).GetComponent<Container>();
		if (!((Object)(object)component == (Object)null) && PrivateArea.CheckAccess(((Component)component).transform.position, 0f, false, true))
		{
			ChestNames.Set(component, text);
		}
	}
}
internal static class ChestPicker
{
	public static Container FindStoreTarget(Vector3 origin, ItemData item, bool mustExist)
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		if (item == null)
		{
			return null;
		}
		float fallback = ((Plugin.Settings != null) ? Plugin.Settings.PlayerDumpRange.Value : 8f);
		Container result = null;
		float num = float.MaxValue;
		foreach (Container item2 in NearbyIndex.Current)
		{
			if (!((Object)(object)item2 == (Object)null))
			{
				float num2 = RulesFile.DumpRange(ContainerFilter.PiecePrefab(item2), fallback);
				float num3 = ContainerFilter.Distance(origin, ((Component)item2).transform.position);
				if (!(num3 > num2) && CanAccept(item2, item, origin, mustExist) && num3 < num)
				{
					num = num3;
					result = item2;
				}
			}
		}
		return result;
	}

	public static bool CanAccept(Container chest, ItemData item, Vector3 from, bool mustExist)
	{
		if ((Object)(object)chest == (Object)null || item == null)
		{
			return false;
		}
		Inventory inventory = chest.GetInventory();
		if (inventory == null || !inventory.CanAddItem(item, item.m_stack))
		{
			return false;
		}
		if (!RulesFile.AllowsStore(ContainerFilter.PiecePrefab(chest), item))
		{
			return false;
		}
		if (mustExist)
		{
			string text = ItemIds.SharedName(item);
			if (string.IsNullOrEmpty(text) || inventory.CountItems(text, -1, true) <= 0)
			{
				return false;
			}
		}
		return true;
	}

	public static List<Container> FindHolding(Vector3 origin, float range, string sharedName)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		List<Container> list = new List<Container>();
		if (string.IsNullOrEmpty(sharedName))
		{
			return list;
		}
		foreach (Container item in NearbyIndex.Within(origin, range))
		{
			Inventory inventory = item.GetInventory();
			if (inventory != null && inventory.CountItems(sharedName, -1, true) > 0)
			{
				list.Add(item);
			}
		}
		list.Sort((Container a, Container b) => ContainerFilter.Distance(origin, ((Component)a).transform.position).CompareTo(ContainerFilter.Distance(origin, ((Component)b).transform.position)));
		return list;
	}
}
internal static class ChestRename
{
	public static bool WantsRename(bool alt)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: 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_004d: Invalid comparison between Unknown and I4
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		if (Plugin.Settings == null || !Plugin.Settings.ModEnabled.Value)
		{
			return false;
		}
		if (alt)
		{
			return true;
		}
		KeyboardShortcut value = Plugin.Settings.RenameKey.Value;
		if ((int)((KeyboardShortcut)(ref value)).MainKey == 0)
		{
			return false;
		}
		if (!KeyUtil.ModifiersHeld(value))
		{
			return false;
		}
		if ((int)((KeyboardShortcut)(ref value)).MainKey != 101)
		{
			return KeyUtil.Held(value);
		}
		return true;
	}

	public static bool TryOpen(Container container = null, bool warnIfMissing = true)
	{
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return false;
		}
		if ((Object)(object)container == (Object)null)
		{
			container = ChestNames.Hovered();
		}
		if ((Object)(object)container == (Object)null || !ChestNames.CanRename(container))
		{
			if (warnIfMissing)
			{
				((Character)localPlayer).Message((MessageType)2, "Look at a chest, then press the rename key.", 0, (Sprite)null, false);
			}
			return false;
		}
		if (!PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, true))
		{
			((Character)localPlayer).Message((MessageType)2, "No access to this chest.", 0, (Sprite)null, false);
			return false;
		}
		ChestRenameReceiver chestRenameReceiver = ((Component)container).GetComponent<ChestRenameReceiver>();
		if ((Object)(object)chestRenameReceiver == (Object)null)
		{
			chestRenameReceiver = ((Component)container).gameObject.AddComponent<ChestRenameReceiver>();
		}
		if ((Object)(object)TextInput.instance == (Object)null)
		{
			return false;
		}
		TextInput.instance.RequestText((TextReceiver)(object)chestRenameReceiver, "Rename chest", 32);
		return true;
	}

	public static string PromptLabel()
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		if (Plugin.Settings == null)
		{
			return "Alt+E";
		}
		string text = KeyUtil.Format(Plugin.Settings.RenameKey.Value);
		if (!string.IsNullOrEmpty(text))
		{
			return text;
		}
		return "Shift+E";
	}
}
internal static class InventoryDump
{
	public static void DumpNearby()
	{
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		if (Plugin.Settings == null || !Plugin.Settings.ModEnabled.Value || !Plugin.Settings.StoreEnabled.Value)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		Inventory inventory = ((Humanoid)localPlayer).GetInventory();
		if (inventory == null)
		{
			return;
		}
		NearbyIndex.Rescan(((Component)localPlayer).transform.position, NearbyIndex.ScanRange());
		List<ItemData> list = new List<ItemData>(inventory.GetAllItems());
		int num = 0;
		foreach (ItemData item in list)
		{
			if (ShouldDump(item, inventory))
			{
				Container val = ChestPicker.FindStoreTarget(((Component)localPlayer).transform.position, item, Plugin.Settings.MustHaveExisting.Value);
				if (!((Object)(object)val == (Object)null) && TransferService.StoreItem(val, inventory, item, item.m_stack))
				{
					num++;
				}
			}
		}
		((Character)localPlayer).Message((MessageType)1, (num > 0) ? Loc.T("Stored " + num + " stacks.", num + " Stapel eingelagert.") : Loc.T("Nothing to store.", "Nichts einzulagern."), 0, (Sprite)null, false);
	}

	public static bool StoreOne(ItemData item)
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || item == null || Plugin.Settings == null)
		{
			return false;
		}
		Inventory inventory = ((Humanoid)localPlayer).GetInventory();
		if (inventory == null)
		{
			return false;
		}
		NearbyIndex.Rescan(((Component)localPlayer).transform.position, NearbyIndex.ScanRange());
		Container val = ChestPicker.FindStoreTarget(((Component)localPlayer).transform.position, item, Plugin.Settings.MustHaveExisting.Value);
		if ((Object)(object)val == (Object)null)
		{
			((Character)localPlayer).Message((MessageType)2, Loc.T("No matching chest.", "Keine passende Truhe."), 0, (Sprite)null, false);
			return false;
		}
		return TransferService.StoreItem(val, inventory, item, item.m_stack);
	}

	public static bool ShouldDump(ItemData item, Inventory inv)
	{
		if (item == null || item.m_stack <= 0)
		{
			return false;
		}
		if (item.m_equipped)
		{
			return false;
		}
		if (Plugin.Settings.IgnoreHotbar.Value && item.m_gridPos.y == 0)
		{
			return false;
		}
		return true;
	}
}
internal static class SearchPing
{
	public static void PingItem(ItemData item)
	{
		if (item != null)
		{
			Search(ItemIds.SharedName(item), ItemIds.PrefabName(item));
		}
	}

	public static void OnCommand(ConsoleEventArgs args)
	{
		string text = ((args != null && args.Length > 1) ? args[1] : null);
		if (string.IsNullOrEmpty(text))
		{
			ItemData hoveredPlayerItem = HoverStore.GetHoveredPlayerItem();
			if (hoveredPlayerItem != null)
			{
				PingItem(hoveredPlayerItem);
			}
			else
			{
				Tell(Loc.T("Usage: storesearch <item>", "Nutzung: storesearch <item>"));
			}
		}
		else
		{
			Search(text, text);
		}
	}

	public static void Search(string sharedOrToken, string prefabHint)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		string text = ItemIds.SharedFromToken(sharedOrToken);
		NearbyIndex.Rescan(((Component)localPlayer).transform.position, NearbyIndex.ScanRange());
		List<Container> list = ChestPicker.FindHolding(((Component)localPlayer).transform.position, NearbyIndex.ScanRange(), text);
		if (list.Count == 0 && !string.IsNullOrEmpty(prefabHint))
		{
			string text2 = ItemIds.SharedFromToken(prefabHint);
			if (text2 != text)
			{
				list = ChestPicker.FindHolding(((Component)localPlayer).transform.position, NearbyIndex.ScanRange(), text2);
			}
		}
		if (list.Count == 0)
		{
			Tell(Loc.T("No nearby chest has that item.", "Keine nahe Truhe hat dieses Item."));
			return;
		}
		Container val = list[0];
		int num = 0;
		foreach (Container item in list)
		{
			Inventory inventory = item.GetInventory();
			if (inventory != null)
			{
				num += inventory.CountItems(text, -1, true);
			}
		}
		TransferService.Highlight(val);
		if ((Object)(object)Chat.instance != (Object)null)
		{
			Chat.instance.SendPing(((Component)val).transform.position);
		}
		string text3 = prefabHint;
		if (Localization.instance != null && !string.IsNullOrEmpty(text))
		{
			text3 = Localization.instance.Localize(text);
		}
		Tell(Loc.T(text3 + ": " + num + " in " + list.Count + " chest(s).", text3 + ": " + num + " in " + list.Count + " Truhe(n)."));
	}

	private static void Tell(string msg)
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer != (Object)null)
		{
			((Character)localPlayer).Message((MessageType)2, msg, 0, (Sprite)null, false);
		}
	}
}
internal static class ContainerFilter
{
	public static bool IsUsable(Container container)
	{
		if ((Object)(object)container == (Object)null)
		{
			return false;
		}
		ZNetView val = Refs.View(container);
		if ((Object)(object)val == (Object)null || !val.IsValid())
		{
			return false;
		}
		if (container.GetInventory() == null)
		{
			return false;
		}
		if ((Object)(object)((Component)container).GetComponent<Piece>() == (Object)null)
		{
			return false;
		}
		return true;
	}

	public static string PiecePrefab(Container container)
	{
		Piece val = (((Object)(object)container != (Object)null) ? ((Component)container).GetComponent<Piece>() : null);
		if ((Object)(object)val == (Object)null)
		{
			return null;
		}
		return ItemIds.StripClone(((Object)((Component)val).gameObject).name);
	}

	public static bool PlayerMayUse(Container container, Vector3 from)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		if (!IsUsable(container))
		{
			return false;
		}
		if (!PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, true))
		{
			return false;
		}
		return true;
	}

	public static float Distance(Vector3 a, Vector3 b)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		return Vector3.Distance(a, b);
	}
}
internal static class ItemIds
{
	public static string PrefabName(ItemData item)
	{
		if (item == null)
		{
			return null;
		}
		if ((Object)(object)item.m_dropPrefab != (Object)null)
		{
			return StripClone(((Object)item.m_dropPrefab).name);
		}
		if (item.m_shared == null || string.IsNullOrEmpty(item.m_shared.m_name) || (Object)(object)ObjectDB.instance == (Object)null)
		{
			return SharedName(item);
		}
		GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.m_shared);
		if ((Object)(object)itemPrefab != (Object)null)
		{
			item.m_dropPrefab = itemPrefab;
			return StripClone(((Object)itemPrefab).name);
		}
		return SharedName(item);
	}

	public static string SharedName(ItemData item)
	{
		if (item?.m_shared == null)
		{
			return null;
		}
		return item.m_shared.m_name;
	}

	public static string StripClone(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return name;
		}
		return name.Replace("(Clone)", string.Empty).Trim();
	}

	public static bool Matches(ItemData item, string token)
	{
		if (item == null || string.IsNullOrEmpty(token))
		{
			return false;
		}
		if (string.Equals(PrefabName(item), token, StringComparison.OrdinalIgnoreCase))
		{
			return true;
		}
		if (string.Equals(SharedName(item), token, StringComparison.OrdinalIgnoreCase))
		{
			return true;
		}
		if (Localization.instance != null && item.m_shared != null && string.Equals(Localization.instance.Localize(item.m_shared.m_name), token, StringComparison.OrdinalIgnoreCase))
		{
			return true;
		}
		return false;
	}

	public static GameObject PrefabFromToken(string token)
	{
		if (string.IsNullOrEmpty(token) || (Object)(object)ObjectDB.instance == (Object)null)
		{
			return null;
		}
		GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(token);
		if ((Object)(object)itemPrefab != (Object)null)
		{
			return itemPrefab;
		}
		foreach (GameObject item in ObjectDB.instance.m_items)
		{
			if ((Object)(object)item == (Object)null)
			{
				continue;
			}
			ItemDrop component = item.GetComponent<ItemDrop>();
			if (component?.m_itemData?.m_shared != null)
			{
				if (string.Equals(component.m_itemData.m_shared.m_name, token, StringComparison.OrdinalIgnoreCase))
				{
					return item;
				}
				if (Localization.instance != null && string.Equals(Localization.instance.Localize(component.m_itemData.m_shared.m_name), token, StringComparison.OrdinalIgnoreCase))
				{
					return item;
				}
			}
		}
		return null;
	}

	public static string SharedFromToken(string token)
	{
		GameObject val = PrefabFromToken(token);
		if ((Object)(object)val == (Object)null)
		{
			return token;
		}
		ItemDrop component = val.GetComponent<ItemDrop>();
		if (component?.m_itemData?.m_shared == null)
		{
			return token;
		}
		return component.m_itemData.m_shared.m_name;
	}
}
internal static class NearbyIndex
{
	private static readonly List<Container> Cached = new List<Container>();

	private static float _nextScan;

	private static Vector3 _lastOrigin;

	private const float RescanMove = 1.5f;

	public static IReadOnlyList<Container> Current => Cached;

	public static void Tick()
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || Plugin.Settings == null || !Plugin.Settings.ModEnabled.Value)
		{
			Cached.Clear();
			return;
		}
		float range = ScanRange();
		Vector3 position = ((Component)localPlayer).transform.position;
		bool flag = Vector3.Distance(position, _lastOrigin) > 1.5f;
		if (!(Time.time < _nextScan) || flag || Cached.Count <= 0)
		{
			Rescan(position, range);
			_lastOrigin = position;
			_nextScan = Time.time + 0.6f;
		}
	}

	public static float ScanRange()
	{
		if (Plugin.Settings == null)
		{
			return 20f;
		}
		return RulesFile.MaxScanRange(Plugin.Settings.StoreRange.Value, Plugin.Settings.CraftRange.Value, Plugin.Settings.PlayerDumpRange.Value);
	}

	public static void Rescan(Vector3 origin, float range)
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		Cached.Clear();
		if (range <= 0f)
		{
			return;
		}
		Collider[] array = Physics.OverlapSphere(origin, range);
		HashSet<int> hashSet = new HashSet<int>();
		Collider[] array2 = array;
		foreach (Collider val in array2)
		{
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			Container componentInParent = ((Component)val).GetComponentInParent<Container>();
			if (!((Object)(object)componentInParent == (Object)null))
			{
				int instanceID = ((Object)componentInParent).GetInstanceID();
				if (hashSet.Add(instanceID) && ContainerFilter.PlayerMayUse(componentInParent, origin))
				{
					Cached.Add(componentInParent);
				}
			}
		}
	}

	public static List<Container> Within(Vector3 origin, float range)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		List<Container> list = new List<Container>();
		foreach (Container item in Cached)
		{
			if (!((Object)(object)item == (Object)null) && ContainerFilter.Distance(origin, ((Component)item).transform.position) <= range)
			{
				list.Add(item);
			}
		}
		return list;
	}

	public static int CountItem(Vector3 origin, float range, string sharedName, bool leaveOne)
	{
		//IL_0056: 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)
		if (string.IsNullOrEmpty(sharedName))
		{
			return 0;
		}
		int num = 0;
		float fallback = ((Plugin.Settings != null) ? Plugin.Settings.CraftRange.Value : range);
		foreach (Container item in Cached)
		{
			if ((Object)(object)item == (Object)null)
			{
				continue;
			}
			string piecePrefab = ContainerFilter.PiecePrefab(item);
			float num2 = RulesFile.CraftRange(piecePrefab, fallback);
			if (ContainerFilter.Distance(origin, ((Component)item).transform.position) > num2)
			{
				continue;
			}
			Inventory inventory = item.GetInventory();
			if (inventory != null && RulesFile.AllowsCraft(piecePrefab, sharedName))
			{
				int num3 = inventory.CountItems(sharedName, -1, true);
				if (leaveOne && num3 > 0)
				{
					num3--;
				}
				if (num3 > 0)
				{
					num += num3;
				}
			}
		}
		return num;
	}
}