Decompiled source of QuickStackToChests v1.0.0

BepInEx/plugins/QuickStackToChests.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("QuickStackToChests")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Valheim BepInEx mod: quick stack inventory into nearby chests by hotkey.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+2d883ccf93068ef6443f7796b032a5faeaf7d882")]
[assembly: AssemblyProduct("QuickStackToChests")]
[assembly: AssemblyTitle("QuickStackToChests")]
[assembly: AssemblyVersion("1.0.0.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 QuickStackToChests
{
	[BepInPlugin("blajion.quickstacktochests", "QuickStackToChests", "1.0.0")]
	[BepInProcess("valheim.exe")]
	public class QuickStackPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "blajion.quickstacktochests";

		public const string PluginName = "QuickStackToChests";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<KeyboardShortcut> HotKey;

		internal static ConfigEntry<float> Radius;

		internal static ConfigEntry<bool> SkipFirstRow;

		internal static ConfigEntry<bool> SkipEquipped;

		internal static ConfigEntry<bool> IncludeNonStackable;

		internal static ConfigEntry<bool> FillEmptySlots;

		internal static ConfigEntry<bool> RespectWards;

		internal static ConfigEntry<bool> SkipContainersInUse;

		internal static ConfigEntry<string> ExcludedItems;

		internal static ConfigEntry<string> ExcludedContainers;

		internal static ConfigEntry<bool> ShowMessage;

		internal static ConfigEntry<bool> VerboseLog;

		private float _nextAllowedRun;

		private bool _settingsOpen;

		private bool _waitingForHotKey;

		private Rect _settingsRect = new Rect(30f, 100f, 460f, 365f);

		private static MethodInfo _chatHasFocus;

		private static bool _chatHasFocusResolved;

		private void Awake()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			HotKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("1. Управление", "HotKey", new KeyboardShortcut((KeyCode)113, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), "Горячая клавиша переноса. Формат BepInEx: 'Q + LeftControl', 'V', 'S + LeftAlt' и т.п.");
			Radius = ((BaseUnityPlugin)this).Config.Bind<float>("2. Поиск сундуков", "Radius", 12f, new ConfigDescription("Радиус поиска сундуков вокруг игрока (метры).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>()));
			SkipContainersInUse = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Поиск сундуков", "SkipContainersInUse", true, "Пропускать сундуки, которые сейчас открыты кем-то (в том числе вами).");
			RespectWards = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Поиск сундуков", "RespectWards", true, "Учитывать ворды (Ward) и приватные сундуки: не трогать чужое.");
			ExcludedContainers = ((BaseUnityPlugin)this).Config.Bind<string>("2. Поиск сундуков", "ExcludedContainers", "", "Префабы сундуков, которые игнорировать. Через запятую, например: piece_chest_private, Container_wood");
			SkipFirstRow = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Предметы", "SkipFirstRow", true, "Не трогать первый ряд инвентаря (хотбар) - там обычно инструменты и еда.");
			SkipEquipped = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Предметы", "SkipEquipped", true, "Не трогать экипированные предметы.");
			IncludeNonStackable = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Предметы", "IncludeNonStackable", false, "Переносить и нестакающиеся предметы (оружие, броня, инструменты), если такие же лежат в сундуке.");
			FillEmptySlots = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Предметы", "FillEmptySlots", true, "Если в сундуке уже есть такой предмет, но стаки заполнены - докладывать остаток в свободные слоты этого сундука.");
			ExcludedItems = ((BaseUnityPlugin)this).Config.Bind<string>("3. Предметы", "ExcludedItems", "", "Предметы, которые никогда не переносить. Через запятую, префаб или имя: Wood, Coins, $item_coins");
			ShowMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Прочее", "ShowMessage", true, "Показывать сообщение о результате переноса на экране.");
			VerboseLog = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Прочее", "VerboseLog", false, "Подробный лог в консоль BepInEx (для отладки).");
			Log.LogInfo((object)string.Format("{0} v{1} loaded. Hotkey: {2}", "QuickStackToChests", "1.0.0", HotKey.Value));
		}

		private void Update()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return;
			}
			if (Input.GetKeyDown((KeyCode)289) && !Console.IsVisible() && !TextInput.IsVisible())
			{
				_settingsOpen = !_settingsOpen;
				_waitingForHotKey = false;
			}
			if (_settingsOpen)
			{
				if (_waitingForHotKey)
				{
					TryReadSideMouseBinding();
				}
			}
			else
			{
				if (Time.time < _nextAllowedRun || IsTypingOrBlocked())
				{
					return;
				}
				KeyboardShortcut value = HotKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					_nextAllowedRun = Time.time + 0.3f;
					try
					{
						QuickStack.Run();
					}
					catch (Exception arg)
					{
						Log.LogError((object)$"Quick stack error: {arg}");
					}
				}
			}
		}

		private void OnGUI()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (_settingsOpen)
			{
				_settingsRect = GUI.Window(731942, _settingsRect, new WindowFunction(DrawSettingsWindow), Translations.Text("QuickStackToChests — Settings", "QuickStackToChests — Настройки"));
			}
		}

		private void DrawSettingsWindow(int windowId)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			GUILayout.Label(Translations.Text("F8 — close. Changes save immediately.", "F8 — закрыть. Настройки сохраняются сразу."), Array.Empty<GUILayoutOption>());
			GUILayout.Space(8f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(Translations.Text("Quick stack hotkey:", "Быстрая сортировка:"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) });
			if (GUILayout.Button(_waitingForHotKey ? Translations.Text("Press a key or side mouse button… (Esc — cancel)", "Нажми клавишу или боковую кнопку мыши… (Esc — отмена)") : ((object)HotKey.Value/*cast due to .constrained prefix*/).ToString(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }))
			{
				_waitingForHotKey = true;
			}
			GUILayout.EndHorizontal();
			if (_waitingForHotKey)
			{
				ReadNewHotKey(Event.current);
			}
			GUILayout.Space(8f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(Translations.Text($"Chest radius: {Radius.Value:0} m", $"Радиус сундуков: {Radius.Value:0} м"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) });
			float num = Mathf.Round(GUILayout.HorizontalSlider(Radius.Value, 1f, 60f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }));
			if (!Mathf.Approximately(num, Radius.Value))
			{
				Radius.Value = num;
				((BaseUnityPlugin)this).Config.Save();
			}
			GUILayout.EndHorizontal();
			DrawToggle(Translations.Text("Skip first inventory row", "Не трогать первый ряд инвентаря"), SkipFirstRow);
			DrawToggle(Translations.Text("Skip equipped items", "Не трогать экипированные вещи"), SkipEquipped);
			DrawToggle(Translations.Text("Respect wards and private chests", "Уважать ворды и приватные сундуки"), RespectWards);
			DrawToggle(Translations.Text("Skip chests currently in use", "Пропускать открытые сундуки"), SkipContainersInUse);
			DrawToggle(Translations.Text("Fill empty slots in matching chests", "Класть остаток в свободные ячейки сундука"), FillEmptySlots);
			DrawToggle(Translations.Text("Include weapons, armor and tools", "Переносить оружие, броню и инструменты"), IncludeNonStackable);
			GUILayout.FlexibleSpace();
			if (GUILayout.Button(Translations.Text("Close (F8)", "Закрыть (F8)"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }))
			{
				_settingsOpen = false;
				_waitingForHotKey = false;
			}
			GUILayout.EndVertical();
			GUI.DragWindow(new Rect(0f, 0f, 10000f, 24f));
		}

		private void DrawToggle(string label, ConfigEntry<bool> setting)
		{
			bool flag = GUILayout.Toggle(setting.Value, label, Array.Empty<GUILayoutOption>());
			if (flag != setting.Value)
			{
				setting.Value = flag;
				((BaseUnityPlugin)this).Config.Save();
			}
		}

		private void ReadNewHotKey(Event currentEvent)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Invalid comparison between Unknown and I4
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Invalid comparison between Unknown and I4
			//IL_0026: 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_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			if (currentEvent == null || (int)currentEvent.type != 4)
			{
				return;
			}
			if ((int)currentEvent.keyCode == 27)
			{
				_waitingForHotKey = false;
				currentEvent.Use();
			}
			else if (!IsModifierKey(currentEvent.keyCode))
			{
				List<KeyCode> list = new List<KeyCode>();
				if (currentEvent.control)
				{
					list.Add((KeyCode)306);
				}
				if (currentEvent.shift)
				{
					list.Add((KeyCode)304);
				}
				if (currentEvent.alt)
				{
					list.Add((KeyCode)308);
				}
				if (currentEvent.command)
				{
					list.Add((KeyCode)310);
				}
				HotKey.Value = new KeyboardShortcut(currentEvent.keyCode, list.ToArray());
				((BaseUnityPlugin)this).Config.Save();
				Log.LogInfo((object)$"New quick stack hotkey: {HotKey.Value}");
				_waitingForHotKey = false;
				currentEvent.Use();
			}
		}

		private void TryReadSideMouseBinding()
		{
			if (Input.GetMouseButtonDown(3))
			{
				SetMouseHotKey((KeyCode)326);
			}
			else if (Input.GetMouseButtonDown(4))
			{
				SetMouseHotKey((KeyCode)327);
			}
			else if (Input.GetMouseButtonDown(5))
			{
				SetMouseHotKey((KeyCode)328);
			}
		}

		private void SetMouseHotKey(KeyCode mouseButton)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			HotKey.Value = new KeyboardShortcut(mouseButton, Array.Empty<KeyCode>());
			((BaseUnityPlugin)this).Config.Save();
			Log.LogInfo((object)$"New quick stack hotkey: {HotKey.Value}");
			_waitingForHotKey = false;
		}

		private static bool IsModifierKey(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Invalid comparison between Unknown and I4
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Invalid comparison between Unknown and I4
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Invalid comparison between Unknown and I4
			if ((int)key != 306 && (int)key != 305 && (int)key != 304 && (int)key != 303 && (int)key != 308 && (int)key != 307 && (int)key != 310)
			{
				return (int)key == 309;
			}
			return true;
		}

		private static bool IsTypingOrBlocked()
		{
			if (Console.IsVisible())
			{
				return true;
			}
			if (TextInput.IsVisible())
			{
				return true;
			}
			if (Menu.IsVisible())
			{
				return true;
			}
			if (IsChatFocused())
			{
				return true;
			}
			return false;
		}

		private static bool IsChatFocused()
		{
			if ((Object)(object)Chat.instance == (Object)null)
			{
				return false;
			}
			if (!_chatHasFocusResolved)
			{
				_chatHasFocusResolved = true;
				_chatHasFocus = typeof(Chat).GetMethod("HasFocus", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, Type.EmptyTypes, null);
			}
			if (_chatHasFocus == null)
			{
				return false;
			}
			try
			{
				object obj = _chatHasFocus.Invoke(Chat.instance, null);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}
	}
	internal static class QuickStack
	{
		private const BindingFlags AllInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private const BindingFlags AllStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly MethodInfo ContainerSaveMethod = typeof(Container).GetMethod("Save", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);

		private static readonly MethodInfo ContainerLoadMethod = typeof(Container).GetMethod("Load", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);

		private static readonly FieldInfo ContainerNViewField = typeof(Container).GetField("m_nview", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo ContainerCheckAccessMethod = typeof(Container).GetMethod("CheckAccess", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(long) }, null);

		private static readonly MethodInfo MoveItemToThisMethod = typeof(Inventory).GetMethod("MoveItemToThis", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[5]
		{
			typeof(Inventory),
			typeof(ItemData),
			typeof(int),
			typeof(int),
			typeof(int)
		}, null);

		private static readonly MethodInfo InventoryChangedNoArgs = typeof(Inventory).GetMethod("Changed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);

		private static readonly MethodInfo InventoryChangedTwoArgs = typeof(Inventory).GetMethod("Changed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
		{
			typeof(bool),
			typeof(bool)
		}, null);

		private static readonly MethodInfo PrivateAreaCheckAccess4 = typeof(PrivateArea).GetMethod("CheckAccess", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[4]
		{
			typeof(Vector3),
			typeof(float),
			typeof(bool),
			typeof(bool)
		}, null);

		private static readonly MethodInfo PrivateAreaCheckAccess3 = typeof(PrivateArea).GetMethod("CheckAccess", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3]
		{
			typeof(Vector3),
			typeof(float),
			typeof(bool)
		}, null);

		private static readonly MethodInfo InCutsceneMethod = typeof(Character).GetMethod("InCutscene", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);

		private static readonly FieldInfo WorldLevelField = typeof(ItemData).GetField("m_worldLevel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static int _layerMask;

		private static readonly Collider[] HitBuffer = (Collider[])(object)new Collider[512];

		internal static void Run()
		{
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || IsInCutscene(localPlayer))
			{
				return;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			if (inventory == null)
			{
				return;
			}
			HashSet<string> excluded = ParseList(QuickStackPlugin.ExcludedItems.Value);
			HashSet<string> excluded2 = ParseList(QuickStackPlugin.ExcludedContainers.Value);
			List<Container> list = FindNearbyContainers(localPlayer, excluded2);
			if (list.Count == 0)
			{
				Message(localPlayer, Translations.NoNearbyChests);
				return;
			}
			int num = 0;
			int num2 = 0;
			foreach (Container item in list)
			{
				Inventory val = PrepareContainer(item);
				if (val == null)
				{
					continue;
				}
				int num3 = 0;
				foreach (ItemData item2 in inventory.GetAllItems().ToList())
				{
					if (IsTransferable(item2, excluded))
					{
						num3 += StackItemIntoContainer(inventory, val, item2);
					}
				}
				if (num3 > 0)
				{
					num += num3;
					num2++;
					FinishContainer(item);
					if (QuickStackPlugin.VerboseLog.Value)
					{
						QuickStackPlugin.Log.LogInfo((object)$"Перенесено {num3} шт. в {((Object)item).name} ({((Component)item).transform.position})");
					}
				}
			}
			if (num > 0)
			{
				NotifyChanged(inventory);
				Message(localPlayer, Translations.Stacked(num, num2));
			}
			else
			{
				Message(localPlayer, Translations.NothingToStack);
			}
		}

		private static List<Container> FindNearbyContainers(Player player, HashSet<string> excluded)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			List<Container> list = new List<Container>();
			HashSet<Container> hashSet = new HashSet<Container>();
			Vector3 center = ((Component)player).transform.position;
			float value = QuickStackPlugin.Radius.Value;
			long playerID = player.GetPlayerID();
			int num = Physics.OverlapSphereNonAlloc(center, value, HitBuffer, GetLayerMask());
			for (int i = 0; i < num; i++)
			{
				Collider val = HitBuffer[i];
				if (!((Object)(object)val == (Object)null))
				{
					Container componentInParent = ((Component)val).GetComponentInParent<Container>();
					if (!((Object)(object)componentInParent == (Object)null) && hashSet.Add(componentInParent) && IsUsableContainer(componentInParent, playerID, excluded))
					{
						list.Add(componentInParent);
					}
				}
			}
			list.Sort((Container a, Container b) => Vector3.SqrMagnitude(((Component)a).transform.position - center).CompareTo(Vector3.SqrMagnitude(((Component)b).transform.position - center)));
			return list;
		}

		private static bool IsUsableContainer(Container container, long playerId, HashSet<string> excluded)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			ZNetView containerNView = GetContainerNView(container);
			if ((Object)(object)containerNView == (Object)null || !containerNView.IsValid())
			{
				return false;
			}
			if ((Object)(object)((Component)container).GetComponentInParent<Player>() != (Object)null)
			{
				return false;
			}
			if (container.GetInventory() == null)
			{
				return false;
			}
			if (QuickStackPlugin.SkipContainersInUse.Value && container.IsInUse())
			{
				return false;
			}
			if (!HasContainerAccess(container, playerId))
			{
				return false;
			}
			if (QuickStackPlugin.RespectWards.Value && !CheckWardAccess(((Component)container).transform.position))
			{
				return false;
			}
			if (excluded.Count > 0)
			{
				string prefabName = Utils.GetPrefabName(((Component)container).gameObject);
				if (excluded.Contains(Normalize(prefabName)) || excluded.Contains(Normalize(container.m_name)))
				{
					return false;
				}
			}
			return true;
		}

		private static ZNetView GetContainerNView(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return null;
			}
			try
			{
				object? obj = ContainerNViewField?.GetValue(container);
				return (ZNetView)(((obj is ZNetView) ? obj : null) ?? container.m_rootObjectOverride ?? ((Component)container).GetComponent<ZNetView>());
			}
			catch (Exception ex)
			{
				QuickStackPlugin.Log.LogWarning((object)("Не удалось получить ZNetView сундука: " + ex.Message));
				return null;
			}
		}

		private static bool HasContainerAccess(Container container, long playerId)
		{
			if (ContainerCheckAccessMethod == null)
			{
				QuickStackPlugin.Log.LogWarning((object)"Container.CheckAccess не найден; сундук пропущен.");
				return false;
			}
			try
			{
				return (bool)ContainerCheckAccessMethod.Invoke(container, new object[1] { playerId });
			}
			catch (Exception ex)
			{
				QuickStackPlugin.Log.LogWarning((object)("Не удалось проверить доступ к сундуку: " + ex.Message));
				return false;
			}
		}

		private static bool CheckWardAccess(Vector3 position)
		{
			//IL_001b: 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)
			try
			{
				if (PrivateAreaCheckAccess4 != null)
				{
					return (bool)PrivateAreaCheckAccess4.Invoke(null, new object[4] { position, 0f, false, false });
				}
				if (PrivateAreaCheckAccess3 != null)
				{
					return (bool)PrivateAreaCheckAccess3.Invoke(null, new object[3] { position, 0f, false });
				}
			}
			catch (Exception ex)
			{
				QuickStackPlugin.Log.LogWarning((object)("PrivateArea.CheckAccess недоступен: " + ex.Message));
			}
			return true;
		}

		private static bool IsInCutscene(Player player)
		{
			if (InCutsceneMethod == null)
			{
				return false;
			}
			try
			{
				return (bool)InCutsceneMethod.Invoke(player, null);
			}
			catch
			{
				return false;
			}
		}

		private static int GetLayerMask()
		{
			if (_layerMask != 0)
			{
				return _layerMask;
			}
			string[] obj = new string[7] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "vehicle", "item" };
			int num = 0;
			string[] array = obj;
			for (int i = 0; i < array.Length; i++)
			{
				int num2 = LayerMask.NameToLayer(array[i]);
				if (num2 >= 0)
				{
					num |= 1 << num2;
				}
			}
			_layerMask = ((num != 0) ? num : (-1));
			return _layerMask;
		}

		private static Inventory PrepareContainer(Container container)
		{
			ZNetView containerNView = GetContainerNView(container);
			if ((Object)(object)containerNView == (Object)null || !containerNView.IsValid())
			{
				return null;
			}
			if (!containerNView.IsOwner())
			{
				containerNView.ClaimOwnership();
				Invoke(ContainerLoadMethod, container, "Container.Load");
			}
			return container.GetInventory();
		}

		private static void FinishContainer(Container container)
		{
			Inventory inventory = container.GetInventory();
			if (inventory != null)
			{
				NotifyChanged(inventory);
			}
			Invoke(ContainerSaveMethod, container, "Container.Save");
		}

		private static void NotifyChanged(Inventory inventory)
		{
			if (inventory == null)
			{
				return;
			}
			try
			{
				if (InventoryChangedNoArgs != null)
				{
					InventoryChangedNoArgs.Invoke(inventory, null);
				}
				else if (InventoryChangedTwoArgs != null)
				{
					InventoryChangedTwoArgs.Invoke(inventory, new object[2] { false, false });
				}
			}
			catch (Exception ex)
			{
				QuickStackPlugin.Log.LogWarning((object)("Inventory.Changed завершился ошибкой: " + ex.Message));
			}
		}

		private static void Invoke(MethodInfo method, Container container, string label)
		{
			if (method == null)
			{
				return;
			}
			try
			{
				method.Invoke(container, null);
			}
			catch (Exception ex)
			{
				QuickStackPlugin.Log.LogWarning((object)(label + " завершился ошибкой: " + ex.Message));
			}
		}

		private static bool IsTransferable(ItemData item, HashSet<string> excluded)
		{
			if (item == null || item.m_shared == null || item.m_stack <= 0)
			{
				return false;
			}
			if (QuickStackPlugin.SkipEquipped.Value && item.m_equipped)
			{
				return false;
			}
			if (QuickStackPlugin.SkipFirstRow.Value && item.m_gridPos.y == 0)
			{
				return false;
			}
			if (item.m_shared.m_questItem)
			{
				return false;
			}
			if (!QuickStackPlugin.IncludeNonStackable.Value && item.m_shared.m_maxStackSize <= 1)
			{
				return false;
			}
			if (excluded.Count > 0)
			{
				string value = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : string.Empty);
				if (excluded.Contains(Normalize(value)) || excluded.Contains(Normalize(item.m_shared.m_name)) || excluded.Contains(Normalize(Localization.instance.Localize(item.m_shared.m_name))))
				{
					return false;
				}
			}
			return true;
		}

		private static bool IsSameItem(ItemData a, ItemData b)
		{
			if (a == null || b == null || a.m_shared == null || b.m_shared == null)
			{
				return false;
			}
			if (a == b)
			{
				return false;
			}
			if (a.m_shared.m_name != b.m_shared.m_name)
			{
				return false;
			}
			if (a.m_quality != b.m_quality || a.m_variant != b.m_variant)
			{
				return false;
			}
			if (WorldLevelField != null)
			{
				object? value = WorldLevelField.GetValue(a);
				object value2 = WorldLevelField.GetValue(b);
				if (!object.Equals(value, value2))
				{
					return false;
				}
			}
			return true;
		}

		private static int StackItemIntoContainer(Inventory from, Inventory to, ItemData item)
		{
			if (!to.GetAllItems().Any((ItemData existing) => IsSameItem(existing, item)))
			{
				return 0;
			}
			int num = 0;
			foreach (ItemData item2 in (from existing in to.GetAllItems()
				where IsSameItem(existing, item) && existing.m_stack < existing.m_shared.m_maxStackSize
				select existing).ToList())
			{
				if (item.m_stack <= 0 || !from.ContainsItem(item))
				{
					break;
				}
				int num2 = item2.m_shared.m_maxStackSize - item2.m_stack;
				if (num2 > 0)
				{
					int num3 = Mathf.Min(num2, item.m_stack);
					if (!MoveStack(from, to, item, num3, item2.m_gridPos.x, item2.m_gridPos.y))
					{
						break;
					}
					num += num3;
				}
			}
			if (QuickStackPlugin.FillEmptySlots.Value)
			{
				int x;
				int y;
				while (item.m_stack > 0 && from.ContainsItem(item) && FindEmptySlot(to, out x, out y))
				{
					int num4 = Mathf.Min(item.m_stack, Mathf.Max(1, item.m_shared.m_maxStackSize));
					if (!MoveStack(from, to, item, num4, x, y))
					{
						break;
					}
					num += num4;
				}
			}
			return num;
		}

		private static bool MoveStack(Inventory from, Inventory to, ItemData item, int amount, int x, int y)
		{
			if (amount <= 0)
			{
				return false;
			}
			if (MoveItemToThisMethod != null)
			{
				try
				{
					if (MoveItemToThisMethod.Invoke(to, new object[5] { from, item, amount, x, y }) is bool result)
					{
						return result;
					}
					return true;
				}
				catch (Exception ex)
				{
					QuickStackPlugin.Log.LogWarning((object)("MoveItemToThis недоступен (" + ex.Message + "), переключаюсь на ручной перенос."));
				}
			}
			return MoveStackFallback(from, to, item, amount, x, y);
		}

		private static bool MoveStackFallback(Inventory from, Inventory to, ItemData item, int amount, int x, int y)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			ItemData itemAt = to.GetItemAt(x, y);
			if (itemAt != null)
			{
				if (!IsSameItem(itemAt, item))
				{
					return false;
				}
				if (itemAt.m_shared.m_maxStackSize - itemAt.m_stack < amount)
				{
					return false;
				}
				itemAt.m_stack += amount;
			}
			else
			{
				ItemData val = item.Clone();
				val.m_stack = amount;
				val.m_gridPos = new Vector2i(x, y);
				if (!to.AddItem(val))
				{
					return false;
				}
			}
			from.RemoveItem(item, amount);
			return true;
		}

		private static bool FindEmptySlot(Inventory inventory, out int x, out int y)
		{
			int width = inventory.GetWidth();
			int height = inventory.GetHeight();
			for (int i = 0; i < height; i++)
			{
				for (int j = 0; j < width; j++)
				{
					if (inventory.GetItemAt(j, i) == null)
					{
						x = j;
						y = i;
						return true;
					}
				}
			}
			x = -1;
			y = -1;
			return false;
		}

		private static HashSet<string> ParseList(string raw)
		{
			HashSet<string> hashSet = new HashSet<string>();
			if (string.IsNullOrEmpty(raw))
			{
				return hashSet;
			}
			string[] array = raw.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = Normalize(array[i]);
				if (text.Length > 0)
				{
					hashSet.Add(text);
				}
			}
			return hashSet;
		}

		private static string Normalize(string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				return value.Trim().ToLowerInvariant();
			}
			return string.Empty;
		}

		private static void Message(Player player, string text)
		{
			if (QuickStackPlugin.ShowMessage.Value && !((Object)(object)player == (Object)null))
			{
				((Character)player).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
		}
	}
	internal static class Translations
	{
		internal static bool IsRussian
		{
			get
			{
				try
				{
					return Localization.instance != null && string.Equals(Localization.instance.GetSelectedLanguage(), "Russian", StringComparison.OrdinalIgnoreCase);
				}
				catch
				{
					return false;
				}
			}
		}

		internal static string NoNearbyChests => Text("No accessible chests nearby", "Рядом нет доступных сундуков");

		internal static string NothingToStack => Text("Nothing to stack", "Нечего раскладывать");

		internal static string Text(string english, string russian)
		{
			if (!IsRussian)
			{
				return english;
			}
			return russian;
		}

		internal static string Stacked(int items, int chests)
		{
			return Text($"Stacked {items} item(s) into {chests} chest(s)", $"Разложено {items} шт. по {chests} сундукам");
		}
	}
}