Decompiled source of NearbyStore v1.1.4

plugins/Jarki-NearbyStore/NearbyStore.dll

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

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("NearbyStore")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.4.0")]
[assembly: AssemblyInformationalVersion("1.1.4+3916ab94dac7f58ae99d82d33ec6c613cbad8063")]
[assembly: AssemblyProduct("NearbyStore")]
[assembly: AssemblyTitle("NearbyStore")]
[assembly: AssemblyVersion("1.1.4.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 NearbyStore
{
	[BepInPlugin("jarki.nearbystore", "NearbyStore", "1.1.4")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private struct TransferRequest
		{
			public Container Chest;

			public bool Answered;

			public bool Granted;

			public bool Loaded;

			public float Deadline;

			public int ItemIndex;
		}

		private static Plugin instance;

		private static readonly FieldRef<Container, ZNetView> View = AccessTools.FieldRefAccess<Container, ZNetView>("m_nview");

		private static readonly Func<Container, bool> LoadContainer = AccessTools.MethodDelegate<Func<Container, bool>>(AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<Container, long, bool> HasAccess = AccessTools.MethodDelegate<Func<Container, long, bool>>(AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null), (object)null, true);

		private static readonly FieldRef<InventoryGui, ItemData> DragItem = AccessTools.FieldRefAccess<InventoryGui, ItemData>("m_dragItem");

		private static readonly FieldRef<InventoryGrid, List<InventoryElement>> Elements = AccessTools.FieldRefAccess<InventoryGrid, List<InventoryElement>>("m_elements");

		private static readonly FieldRef<InventoryGui, float> CraftTimer = AccessTools.FieldRefAccess<InventoryGui, float>("m_craftTimer");

		private const int MaxRequests = 4;

		private const int MaxStepsPerFrame = 32;

		private const float RequestTimeout = 5f;

		private const float OperationTimeout = 15f;

		private static readonly long FrameBudgetTicks = Math.Max(1L, Stopwatch.Frequency / 500);

		private static readonly Predicate<Container> DestroyedContainer = (Container chest) => !Object.op_Implicit((Object)(object)chest);

		private readonly List<Container> chests = new List<Container>();

		private readonly HashSet<Container> outstanding = new HashSet<Container>();

		private readonly HashSet<Container> vanillaOutstanding = new HashSet<Container>();

		private readonly TransferRequest[] requests = new TransferRequest[4];

		private readonly List<ItemData> transferItems = new List<ItemData>();

		private readonly Dictionary<InventoryElement, TMP_Text> markers = new Dictionary<InventoryElement, TMP_Text>();

		private ConfigEntry<float> radius;

		private ConfigEntry<KeyboardShortcut> favoriteKey;

		private Harmony harmony;

		private GameObject panel;

		private Button storeButton;

		private Player actor;

		private bool running;

		private bool skippedTimeout;

		private int index;

		private int moved;

		private int nextRequest;

		private float deadline;

		private float nextPrune;

		private InventoryGrid markerGrid;

		private InventoryElement firstMarkerCell;

		private int markerCellCount;

		private Func<ItemData, bool> protectedItem;

		private void Awake()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			instance = this;
			radius = ((BaseUnityPlugin)this).Config.Bind<float>("Storage", "Radius", 10f, new ConfigDescription("Nearby chest radius in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>()));
			favoriteKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "FavoriteShortcut", new KeyboardShortcut((KeyCode)102, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Toggle favorite on the hovered player-inventory stack while inventory is open.");
			protectedItem = IsProtected;
			harmony = new Harmony("jarki.nearbystore");
			harmony.PatchAll(typeof(Plugin));
		}

		private bool Ready(bool storage = true)
		{
			Player localPlayer = Player.m_localPlayer;
			InventoryGui val = InventoryGui.instance;
			if (Object.op_Implicit((Object)(object)localPlayer) && !((Character)localPlayer).IsDead() && !((Character)localPlayer).IsTeleporting() && !((Character)localPlayer).InCutscene() && Object.op_Implicit((Object)(object)val) && InventoryGui.IsVisible() && DragItem.Invoke(val) == null && !val.m_splitDialog.IsActive && !Menu.IsVisible() && !Console.IsVisible() && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()) && !Minimap.IsOpen() && !TextInput.IsVisible())
			{
				if (storage)
				{
					if (!val.IsContainerOpen() && !Object.op_Implicit((Object)(object)localPlayer.GetCurrentCraftingStation()) && CraftTimer.Invoke(val) < 0f && !val.IsSkillsPanelOpen && !val.IsTextPanelOpen && !val.IsTrophisPanelOpen && !val.IsAchievementsPanelOpen && !((Component)val.m_variantDialog).gameObject.activeInHierarchy && !StoreGui.IsVisible() && (!Object.op_Implicit((Object)(object)TextViewer.instance) || !TextViewer.instance.IsVisible()) && !PlayerCustomizaton.IsBarberGuiVisible() && !Hud.IsPieceSelectionVisible() && !Hud.InRadial())
					{
						return !GameCamera.InFreeFly();
					}
					return false;
				}
				return true;
			}
			return false;
		}

		private bool IsProtected(ItemData item)
		{
			if (!item.m_shared.m_questItem)
			{
				if (Object.op_Implicit((Object)(object)actor))
				{
					return actor.IsEquipActionQueued(item);
				}
				return false;
			}
			return true;
		}

		private void LateUpdate()
		{
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: 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)
			if (Time.unscaledTime >= nextPrune)
			{
				outstanding.RemoveWhere(DestroyedContainer);
				vanillaOutstanding.RemoveWhere(DestroyedContainer);
				nextPrune = Time.unscaledTime + 1f;
			}
			InventoryGui val = InventoryGui.instance;
			if (!Object.op_Implicit((Object)(object)val) || !InventoryGui.IsVisible())
			{
				if (Object.op_Implicit((Object)(object)panel) && panel.activeSelf)
				{
					panel.SetActive(false);
				}
				if (running)
				{
					Finish();
				}
				return;
			}
			EnsurePanel(val);
			bool flag = Ready();
			if (panel.activeSelf != flag)
			{
				panel.SetActive(flag);
			}
			((Selectable)storeButton).interactable = flag && !running;
			if (running && (!flag || (Object)(object)actor != (Object)(object)Player.m_localPlayer))
			{
				Finish();
			}
			if (Ready(storage: false))
			{
				KeyboardShortcut value = favoriteKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					Vector3 pointerPosition = ZInput.pointerPosition;
					ItemData item = val.m_playerGrid.GetItem(new Vector2i((int)pointerPosition.x, (int)pointerPosition.y));
					if (item != null)
					{
						bool flag2 = StorageTransfer.ToggleFavorite(item);
						((Humanoid)Player.m_localPlayer).GetInventory().m_onChanged?.Invoke();
						((Character)Player.m_localPlayer).Message((MessageType)1, (flag2 ? "Favorited: " : "Unfavorited: ") + Localization.instance.Localize(item.m_shared.m_name), 0, (Sprite)null, false);
					}
				}
			}
			if (running)
			{
				Advance();
			}
			UpdateMarkers(val.m_playerGrid);
		}

		private void EnsurePanel(InventoryGui gui)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Expected O, but got Unknown
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: 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_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: 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_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: 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_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Expected O, but got Unknown
			//IL_024c: 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_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Expected O, but got Unknown
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Expected O, but got Unknown
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Expected O, but got Unknown
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: 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_0305: Unknown result type (might be due to invalid IL or missing references)
			//IL_030c: Expected O, but got Unknown
			//IL_031b: Unknown result type (might be due to invalid IL or missing references)
			//IL_032a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_033b: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)panel))
			{
				ClearMarkers();
				panel = new GameObject("NearbyStore", new Type[1] { typeof(RectTransform) });
				RectTransform val = (RectTransform)panel.transform;
				((Transform)val).SetParent((Transform)(object)gui.m_player, false);
				Vector2 val2 = default(Vector2);
				((Vector2)(ref val2))..ctor(1f, 0.5f);
				val.anchorMax = val2;
				val.anchorMin = val2;
				val.pivot = new Vector2(0f, 0.5f);
				val.anchoredPosition = new Vector2(8f, 0f);
				val.sizeDelta = new Vector2(56f, 56f);
				Button craftButton = gui.m_craftButton;
				Image val3 = (Image)((Selectable)craftButton).targetGraphic;
				GameObject val4 = new GameObject("StoreButton", new Type[3]
				{
					typeof(RectTransform),
					typeof(Image),
					typeof(Button)
				});
				RectTransform val5 = (RectTransform)val4.transform;
				((Transform)val5).SetParent((Transform)(object)val, false);
				val5.anchorMin = Vector2.zero;
				val5.anchorMax = Vector2.one;
				val2 = (val5.offsetMin = (val5.offsetMax = Vector2.zero));
				Image component = val4.GetComponent<Image>();
				component.sprite = val3.sprite;
				component.type = val3.type;
				component.pixelsPerUnitMultiplier = val3.pixelsPerUnitMultiplier;
				((Graphic)component).material = ((Graphic)val3).material;
				((Graphic)component).color = ((Graphic)val3).color;
				storeButton = val4.GetComponent<Button>();
				((Selectable)storeButton).targetGraphic = (Graphic)(object)component;
				((Selectable)storeButton).spriteState = ((Selectable)craftButton).spriteState;
				((Selectable)storeButton).colors = ((Selectable)craftButton).colors;
				((Selectable)storeButton).transition = ((Selectable)craftButton).transition;
				((UnityEvent)storeButton.onClick).AddListener(new UnityAction(Begin));
				Color color = default(Color);
				((Color)(ref color))..ctor(1f, 0.73f, 0.3f);
				Color color2 = default(Color);
				((Color)(ref color2))..ctor(1f, 0.94f, 0.72f);
				MakeWandPart(val5, "Wand", new Vector2(-3f, -3f), new Vector2(5f, 30f), -45f, color);
				MakeWandPart(val5, "Tip", new Vector2(8f, 8f), new Vector2(5f, 8f), -45f, color2);
				MakeWandPart(val5, "SparkVertical", new Vector2(13f, 14f), new Vector2(2f, 11f), 0f, color2);
				MakeWandPart(val5, "SparkHorizontal", new Vector2(13f, 14f), new Vector2(11f, 2f), 0f, color2);
				MakeWandPart(val5, "SparkLeft", new Vector2(-13f, 9f), new Vector2(4f, 4f), 45f, color);
				MakeWandPart(val5, "SparkRight", new Vector2(14f, -10f), new Vector2(3f, 3f), 45f, color);
			}
		}

		private static void MakeWandPart(RectTransform parent, string name, Vector2 position, Vector2 size, float rotation, Color color)
		{
			//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_002c: 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_0039: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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_0062: 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_0069: 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_008b: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent((Transform)(object)parent, false);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 0.5f);
			val2.pivot = val3;
			Vector2 anchorMin = (val2.anchorMax = val3);
			val2.anchorMin = anchorMin;
			val2.anchoredPosition = position;
			val2.sizeDelta = size;
			((Transform)val2).localRotation = Quaternion.Euler(0f, 0f, rotation);
			Image component = val.GetComponent<Image>();
			((Graphic)component).color = color;
			((Graphic)component).raycastTarget = false;
		}

		private static TMP_Text MakeText(Transform parent, TMP_Text template, string text, float size)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: 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_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_00ad: 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)
			GameObject val = new GameObject("NearbyStoreText", new Type[1] { typeof(RectTransform) });
			val.SetActive(false);
			TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj).transform.SetParent(parent, false);
			((TMP_Text)obj).font = template.font;
			((TMP_Text)obj).fontSharedMaterial = template.fontSharedMaterial;
			((TMP_Text)obj).fontSize = size;
			((Graphic)obj).color = new Color(1f, 0.84f, 0.45f);
			((TMP_Text)obj).alignment = (TextAlignmentOptions)514;
			((Graphic)obj).raycastTarget = false;
			((TMP_Text)obj).text = text;
			RectTransform rectTransform = ((TMP_Text)obj).rectTransform;
			rectTransform.anchorMin = Vector2.zero;
			rectTransform.anchorMax = Vector2.one;
			Vector2 offsetMin = (rectTransform.offsetMax = Vector2.zero);
			rectTransform.offsetMin = offsetMin;
			val.SetActive(true);
			return (TMP_Text)(object)obj;
		}

		private void ClearMarkers()
		{
			foreach (TMP_Text value in markers.Values)
			{
				if (Object.op_Implicit((Object)(object)value))
				{
					Object.Destroy((Object)(object)((Component)value).gameObject);
				}
			}
			markers.Clear();
		}

		private void UpdateMarkers(InventoryGrid grid)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = grid.GetInventory();
			if (inventory == null)
			{
				return;
			}
			List<InventoryElement> list = Elements.Invoke(grid);
			InventoryElement val = ((list.Count == 0) ? null : list[0]);
			if ((Object)(object)grid != (Object)(object)markerGrid || list.Count != markerCellCount || (Object)(object)val != (Object)(object)firstMarkerCell)
			{
				ClearMarkers();
				markerGrid = grid;
				firstMarkerCell = val;
				markerCellCount = list.Count;
			}
			foreach (InventoryElement item in list)
			{
				ItemData itemAt = inventory.GetItemAt(item.Position.x, item.Position.y);
				bool flag = itemAt != null && StorageTransfer.IsFavorite(itemAt);
				if (!markers.TryGetValue(item, out var value) || !Object.op_Implicit((Object)(object)value))
				{
					if (!flag)
					{
						continue;
					}
					value = MakeText(((Component)item).transform, item.m_amount, "F", 16f);
					value.alignment = (TextAlignmentOptions)257;
					value.rectTransform.offsetMin = new Vector2(4f, 0f);
					value.rectTransform.offsetMax = new Vector2(0f, -3f);
					markers[item] = value;
				}
				((Component)value).gameObject.SetActive(flag);
			}
		}

		private bool Eligible(Container chest)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)chest) && Object.op_Implicit((Object)(object)actor) && !chest.m_autoDestroyEmpty && Object.op_Implicit((Object)(object)((Component)chest).GetComponent<Piece>()) && !Object.op_Implicit((Object)(object)((Component)chest).GetComponent<TombStone>()) && chest.GetInventory() != null && !chest.IsInUse() && (!Object.op_Implicit((Object)(object)chest.m_wagon) || !chest.m_wagon.InUse()))
			{
				Vector3 val = ((Component)chest).transform.position - ((Component)actor).transform.position;
				if (!(((Vector3)(ref val)).sqrMagnitude > radius.Value * radius.Value))
				{
					ZNetView val2 = View.Invoke(chest);
					if (Object.op_Implicit((Object)(object)val2) && val2.IsValid() && HasAccess(chest, actor.GetPlayerID()))
					{
						if (chest.m_checkGuardStone)
						{
							return PrivateArea.CheckAccess(((Component)chest).transform.position, 0f, false, false);
						}
						return true;
					}
					return false;
				}
			}
			return false;
		}

		private bool HasWork()
		{
			List<ItemData> allItems = ((Humanoid)actor).GetInventory().GetAllItems();
			foreach (ItemData transferItem in transferItems)
			{
				if (allItems.Contains(transferItem) && StorageTransfer.CanTransfer(transferItem, protectedItem))
				{
					return true;
				}
			}
			return false;
		}

		private void Begin()
		{
			if (running || !Ready())
			{
				return;
			}
			actor = Player.m_localPlayer;
			index = (moved = (nextRequest = 0));
			skippedTimeout = false;
			transferItems.Clear();
			if (!StorageTransfer.HasTransferableItems(((Humanoid)actor).GetInventory(), protectedItem))
			{
				Finish();
				return;
			}
			transferItems.AddRange(((Humanoid)actor).GetInventory().GetAllItems());
			chests.Clear();
			outstanding.RemoveWhere(DestroyedContainer);
			vanillaOutstanding.RemoveWhere(DestroyedContainer);
			Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
			foreach (Container val in array)
			{
				if (Eligible(val) && !vanillaOutstanding.Contains(val) && (!outstanding.Contains(val) || val.IsOwner()))
				{
					chests.Add(val);
				}
			}
			chests.Sort(delegate(Container a, Container b)
			{
				//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)
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: 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_004b: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val2 = ((Component)a).transform.position - ((Component)actor).transform.position;
				float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
				val2 = ((Component)b).transform.position - ((Component)actor).transform.position;
				return sqrMagnitude.CompareTo(((Vector3)(ref val2)).sqrMagnitude);
			});
			deadline = Time.unscaledTime + 15f;
			running = true;
		}

		private void Advance()
		{
			if (Time.unscaledTime >= deadline)
			{
				Finish(timedOut: true);
				return;
			}
			if (!HasWork())
			{
				Finish();
				return;
			}
			long num = Stopwatch.GetTimestamp() + FrameBudgetTicks;
			int num2 = 0;
			int num3 = 0;
			while (num3 < 32 && Stopwatch.GetTimestamp() < num)
			{
				int num4 = nextRequest;
				nextRequest = (nextRequest + 1) % requests.Length;
				ref TransferRequest reference = ref requests[num4];
				bool flag = false;
				if (!Object.op_Implicit((Object)(object)reference.Chest))
				{
					reference = default(TransferRequest);
					if (index < chests.Count)
					{
						Container val = chests[index++];
						num3++;
						flag = true;
						if (Eligible(val) && !vanillaOutstanding.Contains(val))
						{
							if (val.IsOwner())
							{
								reference.Chest = val;
								reference.Answered = (reference.Granted = true);
								reference.Deadline = Time.unscaledTime + 5f;
							}
							else if (!outstanding.Contains(val))
							{
								reference.Chest = val;
								reference.Deadline = Time.unscaledTime + 5f;
								outstanding.Add(val);
								View.Invoke(val).InvokeRPC("RPC_RequestStack", new object[1] { actor.GetPlayerID() });
							}
						}
					}
				}
				else if (!reference.Loaded && Time.unscaledTime >= reference.Deadline)
				{
					skippedTimeout = true;
					reference = default(TransferRequest);
					flag = true;
				}
				else if (reference.Answered)
				{
					Container chest = reference.Chest;
					if (!reference.Granted || !Eligible(chest) || vanillaOutstanding.Contains(chest))
					{
						reference = default(TransferRequest);
						flag = true;
					}
					else if (chest.IsOwner())
					{
						if (!reference.Loaded)
						{
							LoadContainer(chest);
							reference.Loaded = true;
						}
						if (reference.ItemIndex < transferItems.Count)
						{
							ItemData item = transferItems[reference.ItemIndex++];
							int num5 = StorageTransfer.Deposit(((Humanoid)actor).GetInventory(), chest.GetInventory(), item, protectedItem);
							moved += num5;
							if (num5 > 0)
							{
								num3++;
							}
							if (num5 > 0 && !HasWork())
							{
								Finish();
								return;
							}
						}
						if (reference.ItemIndex >= transferItems.Count)
						{
							reference = default(TransferRequest);
						}
						flag = true;
					}
					else if (reference.Loaded)
					{
						reference = default(TransferRequest);
						flag = true;
					}
				}
				num2 = ((!flag) ? (num2 + 1) : 0);
				if (num2 >= requests.Length)
				{
					break;
				}
			}
			bool flag2 = false;
			TransferRequest[] array = requests;
			for (int i = 0; i < array.Length; i++)
			{
				if (Object.op_Implicit((Object)(object)array[i].Chest))
				{
					flag2 = true;
					break;
				}
			}
			if ((!flag2 && index >= chests.Count) || !HasWork())
			{
				Finish();
			}
		}

		private void Finish(bool timedOut = false)
		{
			running = false;
			Array.Clear(requests, 0, requests.Length);
			bool flag = timedOut || skippedTimeout;
			if (Object.op_Implicit((Object)(object)actor))
			{
				((Character)actor).Message((MessageType)1, flag ? ("Stacked " + moved + " items nearby; some chests timed out. Retry to continue.") : ((moved > 0) ? ("Stacked " + moved + " items nearby.") : "Nothing stacked: no room in matching chests or eligible items."), 0, (Sprite)null, false);
			}
			actor = null;
			chests.Clear();
			transferItems.Clear();
		}

		private bool HasRequest(Container chest)
		{
			TransferRequest[] array = requests;
			for (int i = 0; i < array.Length; i++)
			{
				if ((Object)(object)array[i].Chest == (Object)(object)chest)
				{
					return true;
				}
			}
			return false;
		}

		[HarmonyPatch(typeof(Container), "StackAll")]
		[HarmonyPrefix]
		private static bool VanillaStack(Container __instance)
		{
			if (!Object.op_Implicit((Object)(object)instance))
			{
				return true;
			}
			if (instance.outstanding.Contains(__instance) || instance.HasRequest(__instance) || instance.vanillaOutstanding.Contains(__instance))
			{
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					((Character)Player.m_localPlayer).Message((MessageType)1, "Waiting for this chest's previous stack response.", 0, (Sprite)null, false);
				}
				return false;
			}
			instance.vanillaOutstanding.Add(__instance);
			return true;
		}

		[HarmonyPatch(typeof(Container), "RPC_StackResponse")]
		[HarmonyPrefix]
		private static bool StackResponse(Container __instance, bool granted)
		{
			if (!Object.op_Implicit((Object)(object)instance))
			{
				return true;
			}
			if (instance.vanillaOutstanding.Remove(__instance))
			{
				return true;
			}
			if (!instance.outstanding.Remove(__instance))
			{
				return true;
			}
			if (instance.running)
			{
				for (int i = 0; i < instance.requests.Length; i++)
				{
					ref TransferRequest reference = ref instance.requests[i];
					if (!((Object)(object)reference.Chest != (Object)(object)__instance) && !reference.Answered)
					{
						reference.Answered = true;
						reference.Granted = granted;
						break;
					}
				}
			}
			return false;
		}

		private void OnDestroy()
		{
			if (running)
			{
				Finish();
			}
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if (Object.op_Implicit((Object)(object)panel))
			{
				Object.Destroy((Object)(object)panel);
			}
			ClearMarkers();
			if ((Object)(object)instance == (Object)(object)this)
			{
				instance = null;
			}
		}
	}
	internal static class StorageTransfer
	{
		internal const string FavoriteKey = "jarki.nearbystore.favorite";

		internal static bool IsFavorite(ItemData item)
		{
			return item.m_customData.ContainsKey("jarki.nearbystore.favorite");
		}

		internal static bool ToggleFavorite(ItemData item)
		{
			if (item.m_customData.Remove("jarki.nearbystore.favorite"))
			{
				return false;
			}
			item.m_customData["jarki.nearbystore.favorite"] = "1";
			return true;
		}

		internal static bool CanTransfer(ItemData item, Func<ItemData, bool> protectedItem)
		{
			if (!item.m_equipped && !IsFavorite(item))
			{
				if (protectedItem != null)
				{
					return !protectedItem(item);
				}
				return true;
			}
			return false;
		}

		internal static bool HasTransferableItems(Inventory source, Func<ItemData, bool> protectedItem = null)
		{
			foreach (ItemData allItem in source.GetAllItems())
			{
				if (CanTransfer(allItem, protectedItem))
				{
					return true;
				}
			}
			return false;
		}

		internal static int Deposit(Inventory source, Inventory destination, ItemData item, Func<ItemData, bool> protectedItem = null)
		{
			if (source == destination || !source.GetAllItems().Contains(item) || !CanTransfer(item, protectedItem) || !HasCapacity(destination, item))
			{
				return 0;
			}
			int stack = item.m_stack;
			destination.MoveItemToThis(source, item);
			if (!source.GetAllItems().Contains(item))
			{
				return stack;
			}
			return stack - item.m_stack;
		}

		private static bool HasCapacity(Inventory destination, ItemData item)
		{
			List<ItemData> allItems = destination.GetAllItems();
			bool flag = allItems.Count < destination.GetWidth() * destination.GetHeight();
			foreach (ItemData item2 in allItems)
			{
				if (!(item2.m_shared.m_name != item.m_shared.m_name))
				{
					if (flag)
					{
						return true;
					}
					if (item.m_shared.m_maxStackSize > 1 && item2.m_quality == item.m_quality && item2.m_worldLevel == item.m_worldLevel && item2.m_stack < item2.m_shared.m_maxStackSize)
					{
						return true;
					}
				}
			}
			return false;
		}
	}
}