Decompiled source of FreshDedicatedStorage v1.0.2

BepInEx/plugins/JuStIIFrEsH-FreshDedicatedStorage/FreshStorage.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace JuStIIFrEsH.Valheim.FreshDedicatedStorage
{
	public sealed class BulkBox : MonoBehaviour, Hoverable, Interactable
	{
		private sealed class WithdrawalOption
		{
			internal string Key;

			internal string Label;

			internal BigInteger Amount;
		}

		private sealed class WithdrawalTarget
		{
			internal Vector2i Position;

			internal int Amount;
		}

		private const string StateKey = "freshdedicatedstorage_bulkbox_state_v1";

		private static readonly string LegacyStateKey = 'm' + "ike_bulkbox_state_v1";

		internal static readonly HashSet<BulkBox> Active = new HashSet<BulkBox>();

		private ZNetView view;

		private string cachedRaw;

		private BulkState cachedState;

		private ItemData cachedItem;

		private bool busy;

		private Container assignment;

		private bool assignmentFailed;

		private SpriteRenderer[] itemIcons;

		private string iconTemplate;

		private string selectedWithdrawal = "stack";

		private int altWithdrawalFrame = -1;

		private Container Assignment
		{
			get
			{
				if (!Object.op_Implicit((Object)(object)assignment))
				{
					return assignment = ((Component)this).GetComponentInChildren<Container>();
				}
				return assignment;
			}
		}

		private ZNetView View
		{
			get
			{
				if (!Object.op_Implicit((Object)(object)view))
				{
					return view = ((Component)this).GetComponent<ZNetView>();
				}
				return view;
			}
		}

		public bool ProtectContents
		{
			get
			{
				try
				{
					int result;
					if (!(Read().Count > 0L))
					{
						if (Object.op_Implicit((Object)(object)Assignment))
						{
							Inventory inventory = Assignment.GetInventory();
							result = ((inventory != null && inventory.NrOfItems() > 0) ? 1 : 0);
						}
						else
						{
							result = 0;
						}
					}
					else
					{
						result = 1;
					}
					return (byte)result != 0;
				}
				catch
				{
					return true;
				}
			}
		}

		internal static bool IsWithdrawalWheelActive()
		{
			if (!ZNet.IsSinglePlayer || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !ZInput.GetButton("AltPlace"))
			{
				return false;
			}
			GameObject hoverObject = ((Humanoid)Player.m_localPlayer).GetHoverObject();
			BulkBox bulkBox = (Object.op_Implicit((Object)(object)hoverObject) ? hoverObject.GetComponentInParent<BulkBox>() : null);
			if (Object.op_Implicit((Object)(object)bulkBox) && Object.op_Implicit((Object)(object)bulkBox.View) && bulkBox.View.IsValid())
			{
				return bulkBox.Read().Count > 0L;
			}
			return false;
		}

		internal static float FilterCameraZoomScrollWheel()
		{
			if (!IsWithdrawalWheelActive())
			{
				return ZInput.GetMouseScrollWheel();
			}
			return 0f;
		}

		internal static bool TryCtrlUseHovered()
		{
			if (!IsUseModifierHeld() || !Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return false;
			}
			GameObject hoverObject = ((Humanoid)Player.m_localPlayer).GetHoverObject();
			BulkBox bulkBox = (Object.op_Implicit((Object)(object)hoverObject) ? hoverObject.GetComponentInParent<BulkBox>() : null);
			if (Object.op_Implicit((Object)(object)bulkBox))
			{
				return bulkBox.UseOneForAltUse((Humanoid)(object)Player.m_localPlayer);
			}
			return false;
		}

		private static bool IsUseModifierHeld()
		{
			if (!ZInput.GetKey((KeyCode)306, true))
			{
				return ZInput.GetKey((KeyCode)305, true);
			}
			return true;
		}

		internal static bool TryGetHoveredLinkContainer(out Container container)
		{
			container = null;
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return false;
			}
			GameObject hoverObject = ((Humanoid)Player.m_localPlayer).GetHoverObject();
			BulkBox bulkBox = (Object.op_Implicit((Object)(object)hoverObject) ? hoverObject.GetComponentInParent<BulkBox>() : null);
			if (!Object.op_Implicit((Object)(object)bulkBox) || !Object.op_Implicit((Object)(object)bulkBox.Assignment))
			{
				return false;
			}
			container = bulkBox.Assignment;
			return true;
		}

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

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

		internal string SortingTemplate()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (busy || !Object.op_Implicit((Object)(object)View) || !View.IsValid() || !PrivateArea.CheckAccess(((Component)this).transform.position, 0f, false, false))
			{
				return null;
			}
			if (Object.op_Implicit((Object)(object)Assignment))
			{
				if (!Assignment.IsInUse())
				{
					Inventory inventory = Assignment.GetInventory();
					if (inventory == null || inventory.NrOfItems() <= 0)
					{
						goto IL_0075;
					}
				}
				return null;
			}
			goto IL_0075;
			IL_0075:
			return Read().Template;
		}

		internal bool DepositAssigned(Humanoid user, ItemData item)
		{
			if ((Object)(object)user != (Object)(object)Player.m_localPlayer || !ZNet.IsSinglePlayer || item.m_equipped)
			{
				return false;
			}
			string text = SortingTemplate();
			if (string.IsNullOrEmpty(text) || text != EncodeItem(item))
			{
				return false;
			}
			View.ClaimOwnership();
			if (!View.IsOwner())
			{
				return false;
			}
			busy = true;
			try
			{
				Deposit(user, item);
				return true;
			}
			finally
			{
				busy = false;
			}
		}

		internal string StoreAndCraftLabel()
		{
			if (!Object.op_Implicit((Object)(object)View) || !View.IsValid())
			{
				return "";
			}
			return View.GetZDO().GetString("kac_label", "");
		}

		private int StoreAndCraftLinkId()
		{
			return StoreAndCraftBridge.ParseLinkId(StoreAndCraftLabel());
		}

		private BulkState Read()
		{
			string text = ((Object.op_Implicit((Object)(object)View) && View.IsValid()) ? View.GetZDO().GetString("freshdedicatedstorage_bulkbox_state_v1", "") : "");
			if (string.IsNullOrEmpty(text) && Object.op_Implicit((Object)(object)View) && View.IsValid())
			{
				text = View.GetZDO().GetString(LegacyStateKey, "");
			}
			if (cachedState == null || text != cachedRaw)
			{
				cachedState = BulkState.Decode(text);
				cachedRaw = text;
				cachedItem = null;
			}
			return cachedState;
		}

		public string GetHoverName()
		{
			return "Dedicated Storage Box";
		}

		public float GetHoverOffset()
		{
			return 0.25f;
		}

		public string GetHoverText()
		{
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				BulkState bulkState = Read();
				string text = (string.IsNullOrEmpty(bulkState.Template) ? "Unassigned" : Item(bulkState).m_shared.m_name);
				int num = (StoreAndCraftBridge.IsLinkUiAvailable ? StoreAndCraftLinkId() : 0);
				string text2 = ((num > 0) ? (StoreAndCraftBridge.ColorizeLink(num, "Link: " + num) + "\n") : "") + "Dedicated Storage Box\n" + text + ": <color=yellow>" + bulkState.Count.ToString("N0", CultureInfo.InvariantCulture) + "</color>";
				if (!ZNet.IsSinglePlayer)
				{
					return text2 + "\nSingle-player storage only";
				}
				if (!PrivateArea.CheckAccess(((Component)this).transform.position, 0f, false, false))
				{
					return text2 + "\n$piece_noaccess";
				}
				text2 += (string.IsNullOrEmpty(bulkState.Template) ? "\n[<color=yellow>$KEY_Use</color>] Choose item type" : "\n[<color=yellow>$KEY_Use</color>] Deposit all matching items");
				if (StoreAndCraftBridge.IsLinkUiAvailable)
				{
					text2 += "\n[<color=yellow>Alt + $KEY_Use</color>] Auto-pull settings";
				}
				if (bulkState.Count > 0L)
				{
					WithdrawalOption withdrawalOption = SelectedWithdrawal(bulkState);
					text2 += "\n[<color=yellow>Ctrl + $KEY_Use</color>] Use 1";
					text2 = text2 + "\n[<color=yellow>$KEY_AltPlace + Mouse wheel</color>] Amount: <color=yellow>" + withdrawalOption.Label + "</color>";
					text2 = text2 + "\n[<color=yellow>$KEY_AltPlace + $KEY_Use</color>] Withdraw " + withdrawalOption.Label;
				}
				else
				{
					text2 += "\n[<color=yellow>$KEY_AltPlace + $KEY_Use</color>] Clear item assignment";
				}
				return Localization.instance.Localize(text2);
			}
			catch
			{
				return "Dedicated Storage Box\nStored data unavailable; contents preserved. Check the log.";
			}
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			if (hold)
			{
				return false;
			}
			if ((Object)(object)user == (Object)(object)Player.m_localPlayer && IsUseModifierHeld())
			{
				return UseOneForAltUse(user);
			}
			if ((Object)(object)user == (Object)(object)Player.m_localPlayer && StoreAndCraftBridge.WantsRename())
			{
				return true;
			}
			if (altWithdrawalFrame == Time.frameCount)
			{
				return true;
			}
			return Run(user, delegate
			{
				BulkState bulkState = Read();
				if (Object.op_Implicit((Object)(object)Assignment))
				{
					Inventory inventory = Assignment.GetInventory();
					if (inventory != null && inventory.NrOfItems() > 0)
					{
						OpenAssignment();
						return;
					}
				}
				if (alt)
				{
					if (bulkState.Count == 0L)
					{
						View.GetZDO().Set("freshdedicatedstorage_bulkbox_state_v1", BulkState.Empty.Encode());
						Say(user, "Item assignment cleared.");
					}
					else
					{
						Withdraw(user, bulkState, SelectedWithdrawal(bulkState).Amount);
					}
				}
				else if (string.IsNullOrEmpty(bulkState.Template))
				{
					OpenAssignment();
				}
				else
				{
					BigInteger zero = BigInteger.Zero;
					ItemData[] array = user.GetInventory().GetAllItems().ToArray();
					foreach (ItemData val in array)
					{
						if (!val.m_equipped && val.m_stack > 0 && EncodeItem(val) == Read().Template)
						{
							int stack = val.m_stack;
							Deposit(user, val);
							zero += (BigInteger)stack;
						}
					}
					Say(user, (zero > 0L) ? $"Deposited {zero:N0}." : "No matching items in your inventory.");
				}
			});
		}

		private bool UseOneForAltUse(Humanoid user)
		{
			if (altWithdrawalFrame == Time.frameCount)
			{
				return true;
			}
			altWithdrawalFrame = Time.frameCount;
			return Run(user, delegate
			{
				//IL_006d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0073: Expected O, but got Unknown
				BulkState bulkState = Read();
				if (bulkState.Count <= 0L)
				{
					Say(user, "No stored items to use.");
				}
				else
				{
					ItemData val = Item(bulkState).Clone();
					val.m_stack = 1;
					if (!user.CanConsumeItem(val, true))
					{
						Say(user, "This stored item cannot be used right now.");
					}
					else
					{
						Inventory val2 = new Inventory("Dedicated storage use", (Sprite)null, 1, 1);
						val2.GetAllItems().Add(val);
						user.UseItem(val2, val, true);
						if (!val2.ContainsItem(val))
						{
							View.GetZDO().Set("freshdedicatedstorage_bulkbox_state_v1", bulkState.Withdraw(BigInteger.One).Encode());
						}
					}
				}
			});
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}

		private void OpenAssignment()
		{
			if (!Object.op_Implicit((Object)(object)Assignment) || Assignment.GetInventory() == null || !Object.op_Implicit((Object)(object)InventoryGui.instance))
			{
				throw new InvalidOperationException("Assignment inventory is not ready; try again.");
			}
			assignmentFailed = false;
			Assignment.SetInUse(true);
			InventoryGui.instance.Show(Assignment, 1);
		}

		private void Awake()
		{
			itemIcons = (SpriteRenderer[])(object)new SpriteRenderer[1] { CreateItemIcon("Assigned item icon front", -1f) };
		}

		private void LateUpdate()
		{
			RefreshItemIcon();
			UpdateWithdrawalSelection();
			if (busy || assignmentFailed || !ZNet.IsSinglePlayer || !Object.op_Implicit((Object)(object)View) || !View.IsValid() || !View.IsOwner() || !Object.op_Implicit((Object)(object)Assignment) || Assignment.GetInventory() == null)
			{
				return;
			}
			Inventory inventory = Assignment.GetInventory();
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems.Count == 0)
			{
				return;
			}
			busy = true;
			try
			{
				if (allItems.Count != 1)
				{
					throw new InvalidOperationException("Assignment slot has unexpected contents; retrieve them first.");
				}
				ItemData item = allItems[0];
				if (!Object.op_Implicit((Object)(object)item.m_dropPrefab) || !Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(((Object)item.m_dropPrefab).name)))
				{
					throw new InvalidOperationException("This item cannot be stored safely; take it back from the slot.");
				}
				BulkState next = Read().AssignFromSlot(EncodeItem(item), item.m_stack);
				ChangeInventory(inventory, next, delegate
				{
					if (!inventory.RemoveItem(item))
					{
						throw new InvalidOperationException("Assignment transfer failed.");
					}
				});
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					Say((Humanoid)(object)Player.m_localPlayer, Localization.instance.Localize("Assigned " + item.m_shared.m_name + $"; stored {item.m_stack:N0}."));
				}
			}
			catch (Exception arg)
			{
				assignmentFailed = true;
				Logger.LogError((object)$"Bulk box assignment failed; inspect the saved assignment slot: {arg}");
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					Say((Humanoid)(object)Player.m_localPlayer, "Could not assign that item. Press E to retrieve it from the slot.");
				}
			}
			finally
			{
				busy = false;
			}
		}

		private void UpdateWithdrawalSelection()
		{
			if (!ZNet.IsSinglePlayer || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !ZInput.GetButton("AltPlace"))
			{
				return;
			}
			GameObject hoverObject = ((Humanoid)Player.m_localPlayer).GetHoverObject();
			if (!Object.op_Implicit((Object)(object)hoverObject) || (Object)(object)hoverObject.GetComponentInParent<BulkBox>() != (Object)(object)this)
			{
				return;
			}
			float mouseScrollWheel = ZInput.GetMouseScrollWheel();
			if (Mathf.Abs(mouseScrollWheel) < 0.01f)
			{
				return;
			}
			List<WithdrawalOption> list = WithdrawalOptions(Read());
			if (list.Count != 0)
			{
				int num = list.FindIndex((WithdrawalOption option) => option.Key == selectedWithdrawal);
				if (num < 0)
				{
					num = DefaultWithdrawalIndex(list);
				}
				num = (num + ((mouseScrollWheel > 0f) ? 1 : (-1)) + list.Count) % list.Count;
				selectedWithdrawal = list[num].Key;
			}
		}

		private WithdrawalOption SelectedWithdrawal(BulkState state)
		{
			List<WithdrawalOption> list = WithdrawalOptions(state);
			if (list.Count == 0)
			{
				throw new InvalidOperationException("This box has no items to withdraw.");
			}
			WithdrawalOption withdrawalOption = list.FirstOrDefault((WithdrawalOption option) => option.Key == selectedWithdrawal);
			if (withdrawalOption != null)
			{
				return withdrawalOption;
			}
			withdrawalOption = list[DefaultWithdrawalIndex(list)];
			selectedWithdrawal = withdrawalOption.Key;
			return withdrawalOption;
		}

		private List<WithdrawalOption> WithdrawalOptions(BulkState state)
		{
			List<WithdrawalOption> list = new List<WithdrawalOption>();
			if (state.Count <= 0L)
			{
				return list;
			}
			int num = Math.Max(1, Math.Min(65535, Item(state).m_shared.m_maxStackSize));
			int[] array = new int[6] { 1, 5, 10, 20, 50, 100 };
			for (int i = 0; i < array.Length; i++)
			{
				int num2 = array[i];
				if (num2 < num && state.Count > num2)
				{
					list.Add(new WithdrawalOption
					{
						Key = num2.ToString(CultureInfo.InvariantCulture),
						Label = num2.ToString(CultureInfo.InvariantCulture),
						Amount = num2
					});
				}
			}
			if (state.Count > num)
			{
				list.Add(new WithdrawalOption
				{
					Key = "stack",
					Label = "stack",
					Amount = num
				});
			}
			list.Add(new WithdrawalOption
			{
				Key = "all",
				Label = "all",
				Amount = state.Count
			});
			return list;
		}

		private static int DefaultWithdrawalIndex(List<WithdrawalOption> options)
		{
			int num = options.FindIndex((WithdrawalOption option) => option.Key == "stack");
			if (num < 0)
			{
				return options.Count - 1;
			}
			return num;
		}

		private void RefreshItemIcon()
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			if (itemIcons == null || itemIcons.Length == 0 || !Object.op_Implicit((Object)(object)View) || !View.IsValid() || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			try
			{
				BulkState bulkState = Read();
				if (bulkState.Template == iconTemplate)
				{
					return;
				}
				iconTemplate = bulkState.Template;
				if (string.IsNullOrEmpty(iconTemplate))
				{
					SetItemIconVisible(visible: false);
					return;
				}
				Sprite icon = Item(bulkState).GetIcon();
				if (!Object.op_Implicit((Object)(object)icon))
				{
					SetItemIconVisible(visible: false);
					return;
				}
				Bounds bounds = icon.bounds;
				float x = ((Bounds)(ref bounds)).size.x;
				bounds = icon.bounds;
				float num = Mathf.Max(x, ((Bounds)(ref bounds)).size.y);
				Vector3 localScale = ((num > 0f) ? (Vector3.one * (0.27f / num)) : Vector3.one);
				SpriteRenderer[] array = itemIcons;
				foreach (SpriteRenderer obj in array)
				{
					obj.sprite = icon;
					((Component)obj).transform.localScale = localScale;
					((Renderer)obj).enabled = true;
				}
			}
			catch
			{
				SetItemIconVisible(visible: false);
			}
		}

		private SpriteRenderer CreateItemIcon(string name, float side)
		{
			//IL_0001: 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_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: 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)
			GameObject val = new GameObject(name)
			{
				layer = ((Component)this).gameObject.layer
			};
			val.transform.SetParent(((Component)this).transform, false);
			val.transform.localPosition = new Vector3(0f, ModelAsset.BoxVisualSize.y * 0.48f, side * (ModelAsset.BoxVisualSize.z * 0.5f + 0.008f));
			val.transform.localRotation = ((side < 0f) ? Quaternion.Euler(0f, 180f, 0f) : Quaternion.identity);
			SpriteRenderer obj = val.AddComponent<SpriteRenderer>();
			((Renderer)obj).sortingOrder = 10;
			((Renderer)obj).enabled = false;
			return obj;
		}

		private void SetItemIconVisible(bool visible)
		{
			if (itemIcons == null)
			{
				return;
			}
			SpriteRenderer[] array = itemIcons;
			foreach (SpriteRenderer val in array)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					((Renderer)val).enabled = visible;
				}
			}
		}

		private bool Run(Humanoid user, Action operation)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (busy || (Object)(object)user != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			if (!ZNet.IsSinglePlayer)
			{
				Say(user, "Bulk boxes currently support single-player worlds only.");
				return true;
			}
			if (!Object.op_Implicit((Object)(object)View) || !View.IsValid())
			{
				return true;
			}
			if (!PrivateArea.CheckAccess(((Component)this).transform.position, 0f, true, false))
			{
				return true;
			}
			View.ClaimOwnership();
			if (!View.IsOwner())
			{
				Say(user, "Box not ready; try again.");
				return true;
			}
			busy = true;
			try
			{
				operation();
			}
			catch (InvalidOperationException ex)
			{
				Say(user, ex.Message);
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Bulk box operation failed; stored state retained: {arg}");
				Say(user, "Storage operation failed. Check the log before using this box again.");
			}
			finally
			{
				busy = false;
			}
			return true;
		}

		internal int CraftAvailable(string name, int quality, bool leaveOne)
		{
			if (!ZNet.IsSinglePlayer || string.IsNullOrEmpty(SortingTemplate()))
			{
				return 0;
			}
			BulkState bulkState = Read();
			ItemData val = Item(bulkState);
			if (val.m_shared.m_name != name || (quality >= 0 && val.m_quality != quality) || val.m_worldLevel < Game.m_worldLevel)
			{
				return 0;
			}
			return (int)BigInteger.Min(int.MaxValue, BigInteger.Max(0, bulkState.Count - (leaveOne ? 1 : 0)));
		}

		internal int ConsumeForCraft(string name, int quality, bool leaveOne, int requested)
		{
			int num = Math.Min(requested, CraftAvailable(name, quality, leaveOne));
			if (num <= 0)
			{
				return 0;
			}
			View.ClaimOwnership();
			if (!View.IsOwner())
			{
				return 0;
			}
			View.GetZDO().Set("freshdedicatedstorage_bulkbox_state_v1", Read().Withdraw(num).Encode());
			return num;
		}

		private void Deposit(Humanoid user, ItemData item)
		{
			Inventory inventory = user.GetInventory();
			if (!inventory.ContainsItem(item) || item.m_stack <= 0)
			{
				throw new InvalidOperationException("That item is no longer in your inventory.");
			}
			if (!Object.op_Implicit((Object)(object)item.m_dropPrefab) || !Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(((Object)item.m_dropPrefab).name)))
			{
				throw new InvalidOperationException("This item has no registered prefab and cannot be stored safely.");
			}
			BulkState next = Read().Deposit(EncodeItem(item), item.m_stack);
			ChangeInventory(inventory, next, delegate
			{
				if (!inventory.RemoveItem(item))
				{
					throw new InvalidOperationException("Could not remove the deposited stack.");
				}
			});
		}

		private void Withdraw(Humanoid user, BulkState state, BigInteger requested)
		{
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = user.GetInventory();
			ItemData template = Item(state);
			int num = Math.Max(1, Math.Min(65535, template.m_shared.m_maxStackSize));
			BigInteger bigInteger = BigInteger.Min(state.Count, requested);
			List<WithdrawalTarget> targets = new List<WithdrawalTarget>();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (bigInteger > 0L && allItem.m_stack < num && EncodeItem(allItem) == state.Template)
				{
					int num2 = (int)BigInteger.Min(bigInteger, num - allItem.m_stack);
					targets.Add(new WithdrawalTarget
					{
						Position = allItem.m_gridPos,
						Amount = num2
					});
					bigInteger -= (BigInteger)num2;
				}
			}
			int num3 = 0;
			while (bigInteger > 0L && num3 < inventory.GetHeight())
			{
				int num4 = 0;
				while (bigInteger > 0L && num4 < inventory.GetWidth())
				{
					if (inventory.GetItemAt(num4, num3) == null)
					{
						int num5 = (int)BigInteger.Min(bigInteger, num);
						targets.Add(new WithdrawalTarget
						{
							Position = new Vector2i(num4, num3),
							Amount = num5
						});
						bigInteger -= (BigInteger)num5;
					}
					num4++;
				}
				num3++;
			}
			BigInteger bigInteger2 = BigInteger.Min(state.Count, requested) - bigInteger;
			if (bigInteger2 <= 0L)
			{
				Say(user, "Inventory full; nothing withdrawn.");
				return;
			}
			ChangeInventory(inventory, state.Withdraw(bigInteger2), delegate
			{
				//IL_0036: Unknown result type (might be due to invalid IL or missing references)
				//IL_003c: Expected O, but got Unknown
				foreach (WithdrawalTarget item in targets)
				{
					ItemData val = template.Clone();
					val.m_stack = item.Amount;
					Inventory val2 = new Inventory("Bulk transfer", (Sprite)null, 1, 1);
					val2.GetAllItems().Add(val);
					if (!inventory.MoveItemToThis(val2, val, item.Amount, item.Position.x, item.Position.y))
					{
						throw new InvalidOperationException("Not enough inventory space; nothing withdrawn.");
					}
				}
			});
			Say(user, (bigInteger > 0L) ? $"Withdrew {bigInteger2:N0}; inventory is full." : $"Withdrew {bigInteger2:N0}.");
		}

		private void ChangeInventory(Inventory inventory, BulkState next, Action transfer)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			string text = next.Encode();
			string text2 = View.GetZDO().GetString("freshdedicatedstorage_bulkbox_state_v1", "");
			ZPackage val = new ZPackage();
			inventory.Save(val);
			try
			{
				transfer();
				View.GetZDO().Set("freshdedicatedstorage_bulkbox_state_v1", text);
			}
			catch
			{
				View.GetZDO().Set("freshdedicatedstorage_bulkbox_state_v1", text2);
				inventory.Load(new ZPackage(val.GetArray()));
				throw;
			}
		}

		internal static string EncodeItem(ItemData item)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			ItemData val = item.Clone();
			val.m_stack = 1;
			val.m_gridPos = new Vector2i(0, 0);
			val.m_equipped = false;
			val.m_pickedUp = true;
			val.m_customData = new Dictionary<string, string>();
			foreach (KeyValuePair<string, string> item2 in item.m_customData.OrderBy<KeyValuePair<string, string>, string>((KeyValuePair<string, string> pair) => pair.Key, StringComparer.Ordinal))
			{
				val.m_customData.Add(item2.Key, item2.Value);
			}
			Inventory val2 = new Inventory("Bulk template", (Sprite)null, 1, 1);
			val2.GetAllItems().Add(val);
			ZPackage val3 = new ZPackage();
			val2.Save(val3);
			return Convert.ToBase64String(val3.GetArray());
		}

		private ItemData Item(BulkState state)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Invalid comparison between Unknown and I4
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (cachedItem != null)
			{
				return cachedItem;
			}
			ZPackage val = new ZPackage(Convert.FromBase64String(state.Template));
			Item val2 = (Item)val.ReadInt();
			if ((int)val2 < 108 || val.ReadUShort() != 1)
			{
				throw new FormatException("Invalid item template version/count.");
			}
			(int, ItemData) tuple = ItemData.Load(val, val2);
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(tuple.Item1);
			if (!Object.op_Implicit((Object)(object)itemPrefab))
			{
				throw new InvalidOperationException("Stored item's mod is missing. Restore it before withdrawing.");
			}
			cachedItem = tuple.Item2;
			cachedItem.m_shared = itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared;
			cachedItem.m_dropPrefab = itemPrefab;
			return cachedItem;
		}

		private static void Say(Humanoid user, string text)
		{
			((Character)user).Message((MessageType)2, text, 0, (Sprite)null, false);
		}
	}
	[HarmonyPatch(typeof(Piece), "CanBeRemoved")]
	internal static class BulkRemovalProtection
	{
		private static void Postfix(Piece __instance, ref bool __result)
		{
			BulkBox component = ((Component)__instance).GetComponent<BulkBox>();
			if (Object.op_Implicit((Object)(object)component) && component.ProtectContents)
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "ApplyDamage")]
	internal static class BulkDamageProtection
	{
		private static bool Prefix(WearNTear __instance, ref bool __result)
		{
			BulkBox component = ((Component)__instance).GetComponent<BulkBox>();
			if (!Object.op_Implicit((Object)(object)component) || !component.ProtectContents)
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(WearNTear), "Destroy")]
	internal static class BulkDestroyProtection
	{
		private static bool Prefix(WearNTear __instance)
		{
			BulkBox component = ((Component)__instance).GetComponent<BulkBox>();
			if (Object.op_Implicit((Object)(object)component))
			{
				return !component.ProtectContents;
			}
			return true;
		}
	}
	internal sealed class BulkState
	{
		public BigInteger Count { get; }

		public string Template { get; }

		public static BulkState Empty => new BulkState(BigInteger.Zero, "");

		public BulkState(BigInteger count, string template)
		{
			if (count < 0L)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			if (count > 0L && string.IsNullOrEmpty(template))
			{
				throw new ArgumentException("Missing item template.");
			}
			Count = count;
			Template = template ?? "";
		}

		public string Encode()
		{
			return "1|" + Count.ToString(CultureInfo.InvariantCulture) + "|" + Template;
		}

		public static BulkState Decode(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return Empty;
			}
			string[] array = text.Split('|');
			if (array.Length != 3 || array[0] != "1" || !BigInteger.TryParse(array[1], NumberStyles.None, CultureInfo.InvariantCulture, out var result))
			{
				throw new FormatException("Invalid bulk box data; preserved without modification.");
			}
			if (array[2].Length > 0)
			{
				Convert.FromBase64String(array[2]);
			}
			return new BulkState(result, array[2]);
		}

		public BulkState Deposit(string template, int amount)
		{
			if (amount <= 0)
			{
				throw new ArgumentOutOfRangeException("amount");
			}
			if (!string.IsNullOrEmpty(Template) && Template != template)
			{
				throw new InvalidOperationException("This box is assigned to a different item or variant.");
			}
			return new BulkState(Count + amount, template);
		}

		public BulkState Withdraw(BigInteger amount)
		{
			if (amount <= 0L || amount > Count)
			{
				throw new ArgumentOutOfRangeException("amount");
			}
			return new BulkState(Count - amount, Template);
		}

		public BulkState AssignFromSlot(string template, int amount)
		{
			return (Count.IsZero ? Empty : this).Deposit(template, amount);
		}
	}
	internal sealed class CraftBudget
	{
		internal readonly Dictionary<string, int> Remaining = new Dictionary<string, int>();

		internal void Need(string name, int required, int carried)
		{
			Remaining.TryGetValue(name, out var value);
			Remaining[name] = checked(value + Math.Max(0, required - carried));
		}

		internal void Consumed(string name, int amount)
		{
			if (Remaining.TryGetValue(name, out var value))
			{
				Remaining[name] = Math.Max(0, value - Math.Max(0, amount));
			}
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "DestroyZDO", new Type[] { typeof(ZDO) })]
	internal static class MissingPrefabProtection
	{
		private static bool Prefix(ZDO zdo)
		{
			int prefab = zdo.GetPrefab();
			if (prefab == StringExtensionMethods.GetStableHashCode("M" + "ike_BulkStorageBox"))
			{
				if (Object.op_Implicit((Object)(object)ZNetScene.instance))
				{
					return Object.op_Implicit((Object)(object)ZNetScene.instance.GetPrefab(prefab));
				}
				return false;
			}
			return true;
		}
	}
	internal static class ModelAsset
	{
		internal static readonly Vector3 BoxSize = new Vector3(0.55f, 0.4f, 0.55f);

		internal static readonly Vector3 BoxVisualSize = new Vector3(0.7f, 0.52f, 0.7f);

		internal static readonly Vector3 ColumnSize = new Vector3(0.75f, 1.2f, 0.75f);

		private static readonly Dictionary<string, Color[]> Colors = new Dictionary<string, Color[]>
		{
			["dedicated_storage_box"] = (Color[])(object)new Color[1]
			{
				new Color(0.3f, 0.15f, 0.07f)
			},
			["hugins_reliquary_column"] = (Color[])(object)new Color[1]
			{
				new Color(0.358f, 0.329f, 0.25f)
			}
		};

		internal static Material Material(string model, Material source)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)source) || !Object.op_Implicit((Object)(object)source.shader))
			{
				throw new InvalidOperationException("Base chest shader is unavailable.");
			}
			Color[] array = Colors[model];
			Texture2D val = new Texture2D(array.Length, 1, (TextureFormat)4, false, false)
			{
				filterMode = (FilterMode)0,
				wrapMode = (TextureWrapMode)1
			};
			for (int i = 0; i < array.Length; i++)
			{
				val.SetPixel(i, 0, ((Color)(ref array[i])).gamma);
			}
			val.Apply();
			Material val2 = new Material(source.shader)
			{
				name = model + "_material",
				shaderKeywords = Array.Empty<string>()
			};
			val2.mainTexture = (Texture)(object)val;
			if (val2.HasProperty("_Color"))
			{
				val2.SetColor("_Color", Color.white);
			}
			if (val2.HasProperty("_EmissionColor"))
			{
				val2.SetColor("_EmissionColor", Color.black);
			}
			if (val2.HasProperty("_Glossiness"))
			{
				val2.SetFloat("_Glossiness", 0.12f);
			}
			if (val2.HasProperty("_Metallic"))
			{
				val2.SetFloat("_Metallic", 0f);
			}
			return val2;
		}

		internal static Mesh Mesh(string model, Vector3 size)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: 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_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			//IL_0304: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			//IL_031d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0324: Unknown result type (might be due to invalid IL or missing references)
			//IL_032c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_033b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0342: Unknown result type (might be due to invalid IL or missing references)
			//IL_034a: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Unknown result type (might be due to invalid IL or missing references)
			//IL_036d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_0383: Unknown result type (might be due to invalid IL or missing references)
			//IL_038a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Unknown result type (might be due to invalid IL or missing references)
			//IL_039b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ab: Expected O, but got Unknown
			List<Vector3> list = new List<Vector3>();
			List<Vector2> list2 = new List<Vector2>();
			List<Vector3> list3 = new List<Vector3>();
			List<Vector3> list4 = new List<Vector3>();
			List<Vector2> list5 = new List<Vector2>();
			List<Vector3> list6 = new List<Vector3>();
			List<int> list7 = new List<int>();
			string[] array = Encoding.UTF8.GetString(Read(model + ".obj")).Split('\n');
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Trim().Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
				if (array2.Length == 0)
				{
					continue;
				}
				if (array2[0] == "v")
				{
					list.Add(new Vector3(0f - F(array2[1]), F(array2[2]), F(array2[3])));
				}
				else if (array2[0] == "vt")
				{
					list2.Add(new Vector2(F(array2[1]), F(array2[2])));
				}
				else if (array2[0] == "vn")
				{
					list3.Add(new Vector3(0f - F(array2[1]), F(array2[2]), F(array2[3])));
				}
				else if (array2[0] == "f")
				{
					int count = list4.Count;
					for (int j = 1; j < array2.Length; j++)
					{
						string[] array3 = array2[j].Split('/');
						list4.Add(list[int.Parse(array3[0], CultureInfo.InvariantCulture) - 1]);
						list5.Add(list2[int.Parse(array3[1], CultureInfo.InvariantCulture) - 1]);
						list6.Add(list3[int.Parse(array3[2], CultureInfo.InvariantCulture) - 1]);
					}
					for (int k = 1; k < array2.Length - 2; k++)
					{
						list7.Add(count);
						list7.Add(count + k + 1);
						list7.Add(count + k);
					}
				}
			}
			if (list4.Count == 0 || list7.Count == 0 || list4.Count > 65535)
			{
				throw new InvalidDataException(model + " has invalid runtime geometry.");
			}
			Bounds val = default(Bounds);
			((Bounds)(ref val))..ctor(list4[0], Vector3.zero);
			foreach (Vector3 item in list4)
			{
				((Bounds)(ref val)).Encapsulate(item);
			}
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(size.x / ((Bounds)(ref val)).size.x, size.y / ((Bounds)(ref val)).size.y, size.z / ((Bounds)(ref val)).size.z);
			Vector3 val3 = default(Vector3);
			((Vector3)(ref val3))..ctor(((Bounds)(ref val)).center.x, ((Bounds)(ref val)).min.y, ((Bounds)(ref val)).center.z);
			for (int l = 0; l < list4.Count; l++)
			{
				list4[l] = Vector3.Scale(list4[l] - val3, val2);
				Vector3 val4 = list6[l];
				int index = l;
				Vector3 val5 = new Vector3(val4.x / val2.x, val4.y / val2.y, val4.z / val2.z);
				list6[index] = ((Vector3)(ref val5)).normalized;
			}
			Mesh val6 = new Mesh
			{
				name = model + "_mesh"
			};
			val6.SetVertices(list4);
			val6.SetUVs(0, list5);
			val6.SetNormals(list6);
			val6.SetTriangles(list7, 0);
			val6.RecalculateBounds();
			return val6;
		}

		private static byte[] Read(string name)
		{
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("JuStIIFrEsH.Valheim.FreshDedicatedStorage.Assets." + name);
			using MemoryStream memoryStream = new MemoryStream();
			if (stream == null)
			{
				throw new FileNotFoundException(name);
			}
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}

		private static float F(string value)
		{
			return float.Parse(value, CultureInfo.InvariantCulture);
		}
	}
	[BepInPlugin("justiifresh.valheim.freshdedicatedstorage", "FreshDedicatedStorage", "1.0.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(GameCamera), "UpdateCamera")]
		private static class GameCameraWheelPatch
		{
			private static readonly MethodInfo NativeWheel = AccessTools.Method(typeof(ZInput), "GetMouseScrollWheel", (Type[])null, (Type[])null);

			private static readonly MethodInfo FilteredWheel = AccessTools.Method(typeof(BulkBox), "FilterCameraZoomScrollWheel", (Type[])null, (Type[])null);

			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				foreach (CodeInstruction instruction in instructions)
				{
					if (CodeInstructionExtensions.Calls(instruction, NativeWheel))
					{
						yield return new CodeInstruction(OpCodes.Call, (object)FilteredWheel);
					}
					else
					{
						yield return instruction;
					}
				}
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__FreshStorageTutorial;
		}

		public const string PluginGuid = "justiifresh.valheim.freshdedicatedstorage";

		public const string PluginName = "FreshDedicatedStorage";

		public const string PluginVersion = "1.0.2";

		private static readonly string LegacyBulkBoxPrefabName = "M" + "ike_BulkStorageBox";

		private static readonly string LegacyReliquaryPrefabName = "M" + "ike_SortingTable";

		private static readonly FieldInfo ChatBufferField = typeof(Chat).GetField("m_chatBuffer", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo ChatHideTimerField = typeof(Chat).GetField("m_hideTimer", BindingFlags.Instance | BindingFlags.NonPublic);

		private bool loginTipShown;

		private float playerJoinTime = -1f;

		private void Awake()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_004f: 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_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			Harmony val = new Harmony("justiifresh.valheim.freshdedicatedstorage");
			val.PatchAll(typeof(Plugin).Assembly);
			object obj = <>O.<0>__FreshStorageTutorial;
			if (obj == null)
			{
				ConsoleEvent val2 = FreshStorageTutorial;
				<>O.<0>__FreshStorageTutorial = val2;
				obj = (object)val2;
			}
			new ConsoleCommand("freshstorage", "Shows the FreshDedicatedStorage tutorial", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			try
			{
				StoreAndCraftBridge.Install(val);
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"StoreAndCraft bridge unavailable: {arg}");
			}
			TryInstallDedicatedLinkRedirect(val);
			PrefabManager.OnVanillaPrefabsAvailable += RegisterBulkBox;
			PrefabManager.OnVanillaPrefabsAvailable += RegisterSortingStation;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"FreshDedicatedStorage loaded; waiting for vanilla prefabs.");
		}

		private void Update()
		{
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				loginTipShown = false;
				playerJoinTime = -1f;
				return;
			}
			if (playerJoinTime < 0f)
			{
				playerJoinTime = Time.unscaledTime;
			}
			if (!loginTipShown && Object.op_Implicit((Object)(object)Chat.instance) && Time.unscaledTime - playerJoinTime >= 3f && ChatBufferField?.GetValue(Chat.instance) is List<string> list && Object.op_Implicit((Object)(object)((Terminal)Chat.instance).m_output))
			{
				list.Add("<color=orange>FreshDedicatedStorage:</color> Type /freshstorage for a quick tutorial.");
				((Component)((Terminal)Chat.instance).m_chatWindow).gameObject.SetActive(true);
				ChatHideTimerField?.SetValue(Chat.instance, Mathf.Max(0f, Chat.instance.m_hideDelay - 3f));
				((TMP_Text)((Terminal)Chat.instance).m_output).text = string.Join("\n", list);
				loginTipShown = true;
			}
		}

		private static void FreshStorageTutorial(ConsoleEventArgs args)
		{
			Reply(args, "FreshDedicatedStorage:");
			Reply(args, "  Build Dedicated Storage Boxes from Hammer > Furniture (5 Wood).");
			Reply(args, "  E assigns an empty box or deposits matching inventory items.");
			Reply(args, "  Shift+E withdraws; hold Shift and scroll to choose the amount.");
			Reply(args, "  Ctrl+E uses one stored consumable.");
			Reply(args, "  Hugin's Reliquary stores matching items in assigned boxes within 50 meters.");
			Reply(args, "  Feedback, bugs, or mod integration suggestions: https://justiifresh.com/valheim/");
		}

		private static void Reply(ConsoleEventArgs args, string message)
		{
			Terminal context = args.Context;
			if (context != null)
			{
				context.AddString(message);
			}
		}

		private void TryInstallDedicatedLinkRedirect(Harmony harmony)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			try
			{
				Type type = AccessTools.TypeByName("ChestRename");
				MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "TryOpen", (Type[])null, (Type[])null));
				if (!(methodInfo == null))
				{
					harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "RedirectDedicatedStorageLink", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					StoreAndCraftBridge.EnableLinkUi();
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Dedicated Storage Box link redirect unavailable: " + ex.Message));
			}
		}

		private static void RedirectDedicatedStorageLink(ref Container container)
		{
			if (!Object.op_Implicit((Object)(object)container))
			{
				BulkBox.TryGetHoveredLinkContainer(out container);
			}
		}

		private void OnDestroy()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterBulkBox;
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterSortingStation;
			new Harmony("justiifresh.valheim.freshdedicatedstorage").UnpatchSelf();
		}

		private void RegisterBulkBox()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_0037: 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_004e: Expected O, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Expected O, but got Unknown
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0307: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_0329: Unknown result type (might be due to invalid IL or missing references)
			//IL_0336: Unknown result type (might be due to invalid IL or missing references)
			//IL_0348: Unknown result type (might be due to invalid IL or missing references)
			//IL_035d: Unknown result type (might be due to invalid IL or missing references)
			//IL_036a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0378: Unknown result type (might be due to invalid IL or missing references)
			//IL_038d: Unknown result type (might be due to invalid IL or missing references)
			//IL_039a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_040c: Unknown result type (might be due to invalid IL or missing references)
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterBulkBox;
			try
			{
				PieceConfig val = new PieceConfig
				{
					Name = "Dedicated Storage Box",
					Description = "Stackable single-item storage. Press E on an empty box to select the item type. Single-player only.",
					PieceTable = "Hammer",
					Category = "Furniture",
					CraftingStation = "piece_workbench"
				};
				val.AddRequirement("Wood", 5, true);
				CustomPiece val2 = new CustomPiece(LegacyBulkBoxPrefabName, "piece_chest_wood", val);
				GameObject piecePrefab = val2.PiecePrefab;
				if (!Object.op_Implicit((Object)(object)piecePrefab))
				{
					throw new InvalidOperationException("Could not clone the base building piece.");
				}
				MeshRenderer componentInChildren = piecePrefab.GetComponentInChildren<MeshRenderer>(true);
				if (!Object.op_Implicit((Object)(object)componentInChildren))
				{
					throw new InvalidOperationException("Base material unavailable.");
				}
				Material val3 = ModelAsset.Material("dedicated_storage_box", ((Renderer)componentInChildren).sharedMaterial);
				for (int num = piecePrefab.transform.childCount - 1; num >= 0; num--)
				{
					Object.DestroyImmediate((Object)(object)((Component)piecePrefab.transform.GetChild(num)).gameObject);
				}
				Component[] components = piecePrefab.GetComponents<Component>();
				foreach (Component val4 in components)
				{
					if (!(val4 is Transform) && !(val4 is Piece) && !(val4 is ZNetView) && !(val4 is WearNTear))
					{
						Object.DestroyImmediate((Object)(object)val4);
					}
				}
				piecePrefab.transform.localScale = Vector3.one;
				GameObject val5 = new GameObject("Quaternius Wood Chest")
				{
					layer = piecePrefab.layer
				};
				val5.transform.SetParent(piecePrefab.transform, false);
				val5.AddComponent<MeshFilter>().sharedMesh = ModelAsset.Mesh("dedicated_storage_box", ModelAsset.BoxVisualSize);
				((Renderer)val5.AddComponent<MeshRenderer>()).sharedMaterial = val3;
				AddFrontPreviewMarker(piecePrefab, val3);
				BoxCollider val6 = piecePrefab.AddComponent<BoxCollider>();
				val6.center = new Vector3(0f, ModelAsset.BoxSize.y / 2f, 0f);
				val6.size = ModelAsset.BoxSize;
				WearNTear component = piecePrefab.GetComponent<WearNTear>();
				component.m_new = val5;
				component.m_worn = val5;
				component.m_broken = val5;
				component.m_wet = null;
				component.m_snow = null;
				component.m_snowWorn = null;
				component.m_snowBroken = null;
				component.m_fragmentRoots = (GameObject[])(object)new GameObject[1] { val5 };
				component.m_autoCreateFragments = false;
				component.m_nonSolidRenderers.Clear();
				component.m_supports = true;
				component.m_comOffset = val6.center;
				component.m_health = 100f;
				val2.Piece.m_groundOnly = false;
				val2.Piece.m_groundPiece = false;
				val2.Piece.m_notOnWood = false;
				val2.Piece.m_noClipping = true;
				val2.Piece.m_comfort = 0;
				val2.Piece.m_icon = RenderManager.Instance.Render(piecePrefab, RenderManager.IsometricRotation);
				Vector3 boxSize = ModelAsset.BoxSize;
				AddSnap(piecePrefab, "bottom", Vector3.zero);
				AddSnap(piecePrefab, "top", new Vector3(0f, boxSize.y, 0f));
				AddSnap(piecePrefab, "left", new Vector3((0f - boxSize.x) / 2f, boxSize.y / 2f, 0f));
				AddSnap(piecePrefab, "right", new Vector3(boxSize.x / 2f, boxSize.y / 2f, 0f));
				AddSnap(piecePrefab, "front", new Vector3(0f, boxSize.y / 2f, (0f - boxSize.z) / 2f));
				AddSnap(piecePrefab, "back", new Vector3(0f, boxSize.y / 2f, boxSize.z / 2f));
				piecePrefab.GetComponent<ZNetView>().m_persistent = true;
				piecePrefab.AddComponent<BulkBox>();
				GameObject val7 = new GameObject("Assignment slot");
				val7.transform.SetParent(piecePrefab.transform, false);
				Container obj = val7.AddComponent<Container>();
				obj.m_rootObjectOverride = piecePrefab.GetComponent<ZNetView>();
				obj.m_name = "Choose item type";
				obj.m_width = 1;
				obj.m_height = 1;
				obj.m_privacy = (PrivacySetting)2;
				obj.m_checkGuardStone = true;
				if (!PieceManager.Instance.AddPiece(val2))
				{
					throw new InvalidOperationException("Bulk box registration rejected.");
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Registered DedicatedStorageBox: 0.70m x 0.52m x 0.70m visual on the existing 0.55m x 0.40m x 0.55m snap grid.");
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Dedicated Storage Box registration failed: {arg}");
			}
		}

		private void RegisterSortingStation()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_0037: 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_004e: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterSortingStation;
			try
			{
				PieceConfig val = new PieceConfig
				{
					Name = "Hugin's Reliquary",
					Description = "Stores matching inventory items in assigned dedicated storage within 50 meters. Single-player only.",
					PieceTable = "Hammer",
					Category = "Furniture",
					CraftingStation = "piece_workbench"
				};
				val.AddRequirement("Wood", 10, true);
				CustomPiece val2 = new CustomPiece(LegacyReliquaryPrefabName, "piece_table", val);
				if (!Object.op_Implicit((Object)(object)val2.PiecePrefab))
				{
					throw new InvalidOperationException("Could not clone piece_table.");
				}
				GameObject piecePrefab = val2.PiecePrefab;
				MeshRenderer componentInChildren = piecePrefab.GetComponentInChildren<MeshRenderer>(true);
				if (!Object.op_Implicit((Object)(object)componentInChildren))
				{
					throw new InvalidOperationException("Base material unavailable.");
				}
				Material sharedMaterial = ((Renderer)componentInChildren).sharedMaterial;
				ReplaceGeometry(piecePrefab, "hugins_reliquary_column", ModelAsset.ColumnSize, ModelAsset.Material("dedicated_storage_box", sharedMaterial), out var visual, out var collider);
				AddHuginPerch(piecePrefab);
				WearNTear component = piecePrefab.GetComponent<WearNTear>();
				if (Object.op_Implicit((Object)(object)component))
				{
					component.m_new = visual;
					component.m_worn = visual;
					component.m_broken = visual;
					component.m_wet = null;
					component.m_snow = null;
					component.m_snowWorn = null;
					component.m_snowBroken = null;
					component.m_fragmentRoots = (GameObject[])(object)new GameObject[1] { visual };
					component.m_nonSolidRenderers.Clear();
					component.m_autoCreateFragments = false;
					component.m_comOffset = collider.center;
				}
				val2.Piece.m_comfort = 0;
				val2.Piece.m_icon = RenderManager.Instance.Render(piecePrefab, RenderManager.IsometricRotation);
				piecePrefab.GetComponent<ZNetView>().m_persistent = true;
				piecePrefab.AddComponent<SortingStation>();
				if (!PieceManager.Instance.AddPiece(val2))
				{
					throw new InvalidOperationException("Sorting table registration rejected.");
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Registered HuginsReliquary: 50m range, 10 Wood, Hammer/Furniture.");
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Hugin's Reliquary registration failed: {arg}");
			}
		}

		private static void ReplaceGeometry(GameObject root, string model, Vector3 size, Material material, out GameObject visual, out BoxCollider collider)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			for (int num = root.transform.childCount - 1; num >= 0; num--)
			{
				Object.DestroyImmediate((Object)(object)((Component)root.transform.GetChild(num)).gameObject);
			}
			Component[] components = root.GetComponents<Component>();
			foreach (Component val in components)
			{
				if (!(val is Transform) && !(val is Piece) && !(val is ZNetView) && !(val is WearNTear))
				{
					Object.DestroyImmediate((Object)(object)val);
				}
			}
			root.transform.localScale = Vector3.one;
			visual = new GameObject(model + " visual")
			{
				layer = root.layer
			};
			visual.transform.SetParent(root.transform, false);
			visual.AddComponent<MeshFilter>().sharedMesh = ModelAsset.Mesh(model, size);
			((Renderer)visual.AddComponent<MeshRenderer>()).sharedMaterial = material;
			collider = root.AddComponent<BoxCollider>();
			collider.center = new Vector3(0f, size.y / 2f, 0f);
			collider.size = size;
		}

		private static void AddAssignmentSlot(GameObject root)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Assignment slot");
			val.transform.SetParent(root.transform, false);
			Container obj = val.AddComponent<Container>();
			obj.m_rootObjectOverride = root.GetComponent<ZNetView>();
			obj.m_name = "Choose item type";
			obj.m_width = 1;
			obj.m_height = 1;
			obj.m_privacy = (PrivacySetting)2;
			obj.m_checkGuardStone = true;
		}

		private static void AddSnap(GameObject root, string name, Vector3 position)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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)
			GameObject val = new GameObject("snap_" + name)
			{
				tag = "snappoint"
			};
			val.transform.SetParent(root.transform, false);
			val.transform.localPosition = position;
		}

		private static void AddFrontPreviewMarker(GameObject root, Material material)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("_GhostOnly")
			{
				layer = root.layer
			};
			val.transform.SetParent(root.transform, false);
			val.transform.localPosition = new Vector3(0f, ModelAsset.BoxVisualSize.y + 0.012f, 0f);
			val.AddComponent<MeshFilter>().sharedMesh = FrontArrowMesh();
			Material val2 = new Material(material)
			{
				name = "DedicatedStorageBox_FrontArrow_Bright"
			};
			val2.mainTexture = (Texture)(object)Texture2D.whiteTexture;
			if (val2.HasProperty("_Color"))
			{
				val2.SetColor("_Color", new Color(0.05f, 1f, 1f, 1f));
			}
			if (val2.HasProperty("_EmissionColor"))
			{
				val2.SetColor("_EmissionColor", new Color(0f, 1.5f, 1.5f, 1f));
			}
			((Renderer)val.AddComponent<MeshRenderer>()).sharedMaterial = val2;
			val.SetActive(false);
		}

		private static Mesh FrontArrowMesh()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			Mesh val = new Mesh
			{
				name = "DedicatedStorageBox_FrontArrow"
			};
			val.vertices = (Vector3[])(object)new Vector3[7]
			{
				new Vector3(-0.055f, 0f, 0.105f),
				new Vector3(0.055f, 0f, 0.105f),
				new Vector3(0.055f, 0f, -0.015f),
				new Vector3(0.105f, 0f, -0.015f),
				new Vector3(0f, 0f, -0.165f),
				new Vector3(-0.105f, 0f, -0.015f),
				new Vector3(-0.055f, 0f, -0.015f)
			};
			val.triangles = new int[15]
			{
				0, 1, 2, 0, 2, 6, 6, 2, 3, 6,
				3, 4, 6, 4, 5
			};
			val.normals = (Vector3[])(object)new Vector3[7]
			{
				Vector3.up,
				Vector3.up,
				Vector3.up,
				Vector3.up,
				Vector3.up,
				Vector3.up,
				Vector3.up
			};
			val.RecalculateBounds();
			return val;
		}

		private static void AddHuginPerch(GameObject root)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab = PrefabManager.Instance.GetPrefab("Hugin");
			if (!Object.op_Implicit((Object)(object)prefab))
			{
				Logger.LogWarning((object)"Hugin prefab was unavailable; Hugin's Reliquary will be built without its raven.");
				return;
			}
			GameObject val = Object.Instantiate<GameObject>(prefab);
			((Object)val).name = "Perched Hugin";
			val.transform.SetParent(root.transform, false);
			val.transform.localPosition = Vector3.zero;
			val.transform.localRotation = Quaternion.Euler(0f, 180f, 0f);
			val.transform.localScale = Vector3.one * 0.38f;
			Component[] componentsInChildren = val.GetComponentsInChildren<Component>(true);
			foreach (Component val2 in componentsInChildren)
			{
				if (!(val2 is Transform) && !(val2 is MeshFilter) && !(val2 is MeshRenderer) && !(val2 is SkinnedMeshRenderer))
				{
					Object.DestroyImmediate((Object)(object)val2);
				}
			}
			val.transform.localPosition = new Vector3(0f, 0.9f, 0f);
		}
	}
	public sealed class SortingStation : MonoBehaviour, Hoverable, Interactable
	{
		private bool busy;

		public string GetHoverName()
		{
			return "Hugin's Reliquary";
		}

		public float GetHoverOffset()
		{
			return 0.25f;
		}

		public string GetHoverText()
		{
			return Localization.instance.Localize("Hugin's Reliquary\n[<color=yellow>$KEY_Use</color>] Deposit to storage");
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (hold || busy || (Object)(object)user != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			if (!ZNet.IsSinglePlayer)
			{
				Say(user, "Sorting currently supports single-player worlds only.");
				return true;
			}
			ZNetView component = ((Component)this).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !PrivateArea.CheckAccess(((Component)this).transform.position, 0f, true, false))
			{
				return true;
			}
			busy = true;
			BigInteger zero = BigInteger.Zero;
			HashSet<BulkBox> hashSet = new HashSet<BulkBox>();
			try
			{
				BulkBox[] boxes = (from box in BulkBox.Active.Where(delegate(BulkBox box)
					{
						//IL_0016: Unknown result type (might be due to invalid IL or missing references)
						//IL_0021: Unknown result type (might be due to invalid IL or missing references)
						//IL_0026: Unknown result type (might be due to invalid IL or missing references)
						//IL_002b: Unknown result type (might be due to invalid IL or missing references)
						if (Object.op_Implicit((Object)(object)box) && ((Behaviour)box).isActiveAndEnabled)
						{
							Vector3 val2 = ((Component)box).transform.position - ((Component)this).transform.position;
							return ((Vector3)(ref val2)).sqrMagnitude <= 2500f;
						}
						return false;
					})
					orderby ((Object)box).GetInstanceID()
					select box).ToArray();
				HashSet<ItemData> hashSet2 = new HashSet<ItemData>(user.GetInventory().GetHotbar(false));
				ItemData[] array = user.GetInventory().GetAllItems().ToArray();
				foreach (ItemData val in array)
				{
					if (val.m_equipped || hashSet2.Contains(val) || val.m_stack <= 0)
					{
						continue;
					}
					BulkBox bulkBox = StorageRouting.Nearest(boxes, BulkBox.EncodeItem(val), val.m_equipped, (BulkBox box) => box.SortingTemplate(), delegate(BulkBox box)
					{
						//IL_0006: Unknown result type (might be due to invalid IL or missing references)
						//IL_0011: Unknown result type (might be due to invalid IL or missing references)
						//IL_0016: Unknown result type (might be due to invalid IL or missing references)
						//IL_001b: Unknown result type (might be due to invalid IL or missing references)
						Vector3 val2 = ((Component)box).transform.position - ((Component)this).transform.position;
						return ((Vector3)(ref val2)).sqrMagnitude;
					});
					if (Object.op_Implicit((Object)(object)bulkBox))
					{
						int stack = val.m_stack;
						if (bulkBox.DepositAssigned(user, val))
						{
							zero += (BigInteger)stack;
							hashSet.Add(bulkBox);
						}
					}
				}
				Say(user, (zero > 0L) ? $"Stored {zero:N0} items in {hashSet.Count} boxes." : "No matching items and available assigned boxes within 50 m.");
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Sorting stopped after storing {zero} items: {arg}");
				Say(user, $"Stored {zero:N0} items; sorting stopped on an error. Check the log.");
			}
			finally
			{
				busy = false;
			}
			return true;
		}

		private static void Say(Humanoid user, string message)
		{
			((Character)user).Message((MessageType)2, message, 0, (Sprite)null, false);
		}
	}
	internal static class StorageRouting
	{
		internal const float Range = 50f;

		internal static T Nearest<T>(IEnumerable<T> boxes, string template, bool equipped, Func<T, string> assignment, Func<T, float> distanceSquared) where T : class
		{
			if (equipped || string.IsNullOrEmpty(template))
			{
				return null;
			}
			T val = null;
			float num = 2500f;
			foreach (T box in boxes)
			{
				float num2 = distanceSquared(box);
				if (!(num2 > num) && !float.IsNaN(num2) && !(assignment(box) != template) && (val == null || num2 != num))
				{
					val = box;
					num = num2;
				}
			}
			return val;
		}
	}
	internal static class StoreAndCraftBridge
	{
		private static Type plugin;

		private static Type staging;

		private static Type buildGrab;

		private static Type stationFeed;

		private static Type stationLink;

		private static MethodInfo wantsRename;

		private static MethodInfo activePullRange;

		private static MethodInfo parseLink;

		private static MethodInfo notePulseConsumed;

		private static MethodInfo colorizeLink;

		private static PropertyInfo activeLink;

		private static bool installed;

		private static bool linkUiAvailable;

		private static CraftBudget current;

		internal static bool IsAvailable => installed;

		internal static bool IsLinkUiAvailable => linkUiAvailable;

		private static object Settings => AccessTools.Property(plugin, "Settings").GetValue(null);

		internal static void EnableLinkUi()
		{
			linkUiAvailable = true;
		}

		internal static int ParseLinkId(string label)
		{
			if (parseLink == null)
			{
				return 0;
			}
			try
			{
				return Convert.ToInt32(parseLink.Invoke(null, new object[1] { label }));
			}
			catch
			{
				return 0;
			}
		}

		internal static string ColorizeLink(int linkId, string text)
		{
			if (colorizeLink == null)
			{
				return text;
			}
			try
			{
				return (colorizeLink.Invoke(null, new object[2] { linkId, text }) as string) ?? text;
			}
			catch
			{
				return text;
			}
		}

		private static T Setting<T>(string name)
		{
			return ((ConfigEntry<T>)AccessTools.Property(Settings.GetType(), name).GetValue(Settings)).Value;
		}

		private static bool Enabled(Player player)
		{
			if (Object.op_Implicit((Object)(object)player) && (Object)(object)player == (Object)(object)Player.m_localPlayer && ZNet.IsSinglePlayer && (bool)AccessTools.Property(staging, "Active").GetValue(null))
			{
				return !(bool)AccessTools.Method(buildGrab, "ShouldGrab", (Type[])null, (Type[])null).Invoke(null, new object[1] { player });
			}
			return false;
		}

		private static BulkBox[] Boxes(Player player)
		{
			return Boxes(player, Setting<float>("CraftRange"));
		}

		private static BulkBox[] Boxes(Player player, float range)
		{
			return BulkBox.Active.Where((BulkBox box) => Object.op_Implicit((Object)(object)box) && ((Behaviour)box).isActiveAndEnabled && Vector3.Distance(((Component)player).transform.position, ((Component)box).transform.position) <= range).OrderBy(delegate(BulkBox box)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Component)box).transform.position - ((Component)player).transform.position;
				return ((Vector3)(ref val)).sqrMagnitude;
			}).ToArray();
		}

		internal static bool WantsRename()
		{
			if (!installed || wantsRename == null)
			{
				return false;
			}
			try
			{
				return (bool)wantsRename.Invoke(null, new object[1] { false });
			}
			catch
			{
				return false;
			}
		}

		internal static void Install(Harmony harmony)
		{
			installed = false;
			if (Chainloader.PluginInfos.TryGetValue("com.morda.storeandcraft", out var value))
			{
				Assembly assembly = ((object)value.Instance).GetType().Assembly;
				plugin = assembly.GetType("StoreAndCraft.Plugin", throwOnError: true);
				staging = assembly.GetType("StoreAndCraft.StagingPull", throwOnError: true);
				buildGrab = assembly.GetType("StoreAndCraft.BuildGrab", throwOnError: true);
				stationFeed = assembly.GetType("StoreAndCraft.StationFeed", throwOnError: true);
				stationLink = assembly.GetType("StoreAndCraft.StationLink", throwOnError: true);
				MethodInfo methodInfo = AccessTools.Method(assembly.GetType("StoreAndCraft.RequirementBridge", throwOnError: true), "CountNearby", (Type[])null, (Type[])null);
				MethodInfo methodInfo2 = AccessTools.Method(staging, "ConsumeRequirements", (Type[])null, (Type[])null);
				MethodInfo methodInfo3 = AccessTools.Method(assembly.GetType("StoreAndCraft.TransferService", throwOnError: true), "Consume", (Type[])null, (Type[])null);
				MethodInfo methodInfo4 = AccessTools.Method(stationFeed, "ChestsHave", (Type[])null, (Type[])null);
				MethodInfo methodInfo5 = AccessTools.Method(stationFeed, "ConsumeFromChests", (Type[])null, (Type[])null);
				wantsRename = AccessTools.Method(assembly.GetType("StoreAndCraft.ChestRename", throwOnError: true), "WantsRename", (Type[])null, (Type[])null);
				activePullRange = AccessTools.Method(stationFeed, "ActivePullRange", (Type[])null, (Type[])null);
				parseLink = AccessTools.Method(stationLink, "ParseFromName", (Type[])null, (Type[])null);
				colorizeLink = AccessTools.Method(stationLink, "Colorize", (Type[])null, (Type[])null);
				notePulseConsumed = AccessTools.Method(stationFeed, "NotePulseConsumed", (Type[])null, (Type[])null);
				activeLink = AccessTools.Property(stationLink, "ActiveId");
				if (methodInfo == null || methodInfo2 == null || methodInfo3 == null || methodInfo4 == null || methodInfo5 == null || wantsRename == null || activePullRange == null || parseLink == null || colorizeLink == null || notePulseConsumed == null || activeLink == null)
				{
					throw new InvalidOperationException("Unsupported StoreAndCraft API.");
				}
				harmony.Patch((MethodBase)methodInfo2, Patch("Begin"), Patch("End"), (HarmonyMethod)null, Patch("Cleanup"), (HarmonyMethod)null);
				harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, Patch("ChestConsumed"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, Patch("Count"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, Patch("StationHas"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)methodInfo5, (HarmonyMethod)null, Patch("ConsumeForStation"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				installed = true;
				Logger.LogInfo((object)"Dedicated storage crafting and station-link bridge enabled.");
			}
		}

		private static HarmonyMethod Patch(string method)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			return new HarmonyMethod(typeof(StoreAndCraftBridge), method, (Type[])null);
		}

		private static void Count(Player player, string sharedName, int quality, ref int __result)
		{
			if (Enabled(player))
			{
				long num = Math.Max(0, __result);
				BulkBox[] array = Boxes(player);
				foreach (BulkBox bulkBox in array)
				{
					num = Math.Min(1073741823L, num + bulkBox.CraftAvailable(sharedName, quality, Setting<bool>("LeaveOneItem")));
				}
				__result = (int)num;
			}
		}

		private static void Begin(Player player, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier)
		{
			current = null;
			if (!Enabled(player) || requirements == null)
			{
				return;
			}
			CraftBudget craftBudget = new CraftBudget();
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			foreach (Requirement val in requirements)
			{
				if (val != null && Object.op_Implicit((Object)(object)val.m_resItem) && (!Object.op_Implicit((Object)(object)currentCraftingStation) || currentCraftingStation.m_upgrader == val.m_upgraderResource) && (Object.op_Implicit((Object)(object)currentCraftingStation) || !val.m_upgraderResource))
				{
					string name = val.m_resItem.m_itemData.m_shared.m_name;
					int carried = (from item in ((Humanoid)player).GetInventory().GetAllItems()
						where item.m_shared.m_name == name && (itemQuality < 0 || item.m_quality == itemQuality) && item.m_worldLevel >= Game.m_worldLevel
						select item).Sum((ItemData item) => item.m_stack);
					craftBudget.Need(name, checked(val.GetAmount(qualityLevel) * Math.Max(1, multiplier)), carried);
				}
			}
			current = craftBudget;
		}

		private static void ChestConsumed(string sharedName, int __result)
		{
			current?.Consumed(sharedName, __result);
		}

		private static void End(Player player, int itemQuality)
		{
			CraftBudget craftBudget = current;
			current = null;
			if (craftBudget == null)
			{
				return;
			}
			BulkBox[] array = Boxes(player);
			foreach (KeyValuePair<string, int> item in craftBudget.Remaining)
			{
				int num = item.Value;
				BulkBox[] array2 = array;
				foreach (BulkBox bulkBox in array2)
				{
					if (num <= 0)
					{
						break;
					}
					num -= bulkBox.ConsumeForCraft(item.Key, itemQuality, Setting<bool>("LeaveOneItem"), num);
				}
			}
		}

		private static Exception Cleanup(Exception __exception)
		{
			current = null;
			return __exception;
		}

		private static int ActiveLinkId()
		{
			return Convert.ToInt32(activeLink.GetValue(null));
		}

		private static bool LinkAllows(BulkBox box, int stationLinkId)
		{
			if (stationLinkId < 0)
			{
				return true;
			}
			int num = Convert.ToInt32(parseLink.Invoke(null, new object[1] { box.StoreAndCraftLabel() }));
			if (stationLinkId != 0)
			{
				return num == stationLinkId;
			}
			return num == 0;
		}

		private static BulkBox[] LinkedStationBoxes(Player player)
		{
			float range = Convert.ToSingle(activePullRange.Invoke(null, null));
			int stationLinkId = ActiveLinkId();
			return (from box in Boxes(player, range)
				where LinkAllows(box, stationLinkId)
				select box).ToArray();
		}

		private static void StationHas(Player player, string shared, ref bool __result)
		{
			if (!__result && Enabled(player) && !string.IsNullOrEmpty(shared))
			{
				bool leaveOne = Setting<bool>("LeaveOneItem");
				__result = LinkedStationBoxes(player).Any((BulkBox box) => box.CraftAvailable(shared, -1, leaveOne) > 0);
			}
		}

		private static void ConsumeForStation(Player player, string shared, int amount, ref int __result)
		{
			if (!Enabled(player) || string.IsNullOrEmpty(shared) || amount <= __result)
			{
				return;
			}
			int num = amount - Math.Max(0, __result);
			bool leaveOne = Setting<bool>("LeaveOneItem");
			BulkBox[] array = LinkedStationBoxes(player);
			foreach (BulkBox bulkBox in array)
			{
				if (num > 0)
				{
					int num2 = bulkBox.ConsumeForCraft(shared, -1, leaveOne, num);
					if (num2 > 0)
					{
						__result += num2;
						num -= num2;
						notePulseConsumed.Invoke(null, new object[2] { shared, num2 });
					}
					continue;
				}
				break;
			}
		}
	}
}