Decompiled source of ReservedItemSlotCore v2.0.39

ReservedItemSlotCore.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalCompanyInputUtils.Api;
using ReservedItemSlotCore.Compatibility;
using ReservedItemSlotCore.Config;
using ReservedItemSlotCore.Data;
using ReservedItemSlotCore.Input;
using ReservedItemSlotCore.Networking;
using ReservedItemSlotCore.Patches;
using TMPro;
using TooManyEmotes;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ReservedItemSlotCore")]
[assembly: AssemblyDescription("Mod made by flipf17")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ReservedItemSlotCore")]
[assembly: AssemblyCopyright("Copyright ©  2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("238ce080-e339-46b6-9b08-992a950453a1")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: InternalsVisibleTo("ReservedFlashlightSlot")]
[assembly: InternalsVisibleTo("ReservedWalkieSlot")]
[assembly: InternalsVisibleTo("ReservedWeaponSlot")]
[assembly: InternalsVisibleTo("ReservedSprayPaintSlot")]
[assembly: InternalsVisibleTo("ReservedUtilitySlot")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ReservedItemSlotCore
{
	[HarmonyPatch]
	internal static class ItemNameMap
	{
		private static Dictionary<string, Item> originalNameToItemMap = new Dictionary<string, Item>();

		private static Dictionary<Item, string> itemToNameMap = new Dictionary<Item, string>();

		[HarmonyPatch(typeof(StartOfRound), "Start")]
		[HarmonyPrefix]
		private static void RecordOriginalItemNames(StartOfRound __instance)
		{
			List<Item> list = __instance?.allItemsList?.itemsList;
			if (list == null)
			{
				Plugin.LogError("Failed to record original item names. This might be fine if you're not using translation/localization mods. (no guarantees)");
				return;
			}
			foreach (Item item in list)
			{
				string text = item?.itemName;
				if (!string.IsNullOrEmpty(text))
				{
					if (!itemToNameMap.ContainsKey(item))
					{
						itemToNameMap.Add(item, text);
					}
					if (!originalNameToItemMap.ContainsKey(text))
					{
						originalNameToItemMap.Add(text, item);
					}
				}
			}
		}

		internal static string GetItemName(GrabbableObject grabbableObject)
		{
			if ((Object)(object)grabbableObject?.itemProperties == (Object)null)
			{
				return "";
			}
			string itemName = GetItemName(grabbableObject.itemProperties);
			return (itemName != null) ? itemName : "";
		}

		internal static string GetItemName(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return "";
			}
			if (itemToNameMap.TryGetValue(item, out var value) && value != null)
			{
				return value;
			}
			return "";
		}
	}
	[BepInPlugin("FlipMods.ReservedItemSlotCore", "ReservedItemSlotCore", "2.0.39")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	internal class Plugin : BaseUnityPlugin
	{
		private Harmony _harmony;

		public static Plugin instance;

		private static ManualLogSource logger;

		public static List<ReservedItemSlotData> customItemSlots = new List<ReservedItemSlotData>();

		private void Awake()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			instance = this;
			CreateCustomLogger();
			ConfigSettings.BindConfigSettings();
			AddCustomItemSlots();
			if (InputUtilsCompat.Enabled)
			{
				InputUtilsCompat.Init();
			}
			_harmony = new Harmony("ReservedItemSlotCore");
			PatchAll();
			Log("ReservedItemSlotCore loaded");
		}

		private void AddCustomItemSlots()
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			foreach (CustomItemSlotConfigEntry customItemSlotConfig in ConfigSettings.customItemSlotConfigs)
			{
				if (!(customItemSlotConfig.customItemSlotName == "") && customItemSlotConfig.customItemSlotItems.Length != 0)
				{
					ReservedItemSlotData reservedItemSlotData = ReservedItemSlotData.CreateReservedItemSlotData(customItemSlotConfig.customItemSlotName, customItemSlotConfig.customItemSlotPriority, customItemSlotConfig.customItemSlotPrice);
					string[] customItemSlotItems = customItemSlotConfig.customItemSlotItems;
					foreach (string itemName in customItemSlotItems)
					{
						ReservedItemData itemData = new ReservedItemData(itemName);
						reservedItemSlotData.AddItemToReservedItemSlot(itemData);
					}
					customItemSlots.Add(reservedItemSlotData);
				}
			}
		}

		private void PatchAll()
		{
			IEnumerable<Type> enumerable;
			try
			{
				enumerable = Assembly.GetExecutingAssembly().GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				enumerable = ex.Types.Where((Type t) => t != null);
			}
			foreach (Type item in enumerable)
			{
				_harmony.PatchAll(item);
			}
		}

		private void CreateCustomLogger()
		{
			try
			{
				logger = Logger.CreateLogSource(string.Format("{0}-{1}", "ReservedItemSlotCore", "2.0.39"));
			}
			catch
			{
				logger = ((BaseUnityPlugin)this).Logger;
			}
		}

		public static void Log(string message)
		{
			logger.LogInfo((object)message);
		}

		public static void LogError(string message)
		{
			logger.LogError((object)message);
		}

		public static void LogWarning(string message)
		{
			logger.LogWarning((object)message);
		}

		public static bool IsModLoaded(string guid)
		{
			return Chainloader.PluginInfos.ContainsKey(guid);
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "FlipMods.ReservedItemSlotCore";

		public const string PLUGIN_NAME = "ReservedItemSlotCore";

		public const string PLUGIN_VERSION = "2.0.39";
	}
	[HarmonyPatch]
	public static class ReservedHotbarManager
	{
		public static int indexInHotbar = 0;

		public static int indexInReservedHotbar = 0;

		internal static List<ReservedItemSlotData> currentlyToggledItemSlots = new List<ReservedItemSlotData>();

		public static PlayerControllerB localPlayerController => StartOfRound.Instance?.localPlayerController;

		public static ReservedPlayerData localPlayerData => ReservedPlayerData.localPlayerData;

		public static int reservedHotbarSize => SessionManager.numReservedItemSlotsUnlocked;

		public static bool isToggledInReservedSlots
		{
			get
			{
				ReservedItemSlotData currentlySelectedReservedItemSlot = localPlayerData.GetCurrentlySelectedReservedItemSlot();
				return (ReservedPlayerData.localPlayerData.inReservedHotbarSlots && Keybinds.pressedToggleKey) || (currentlyToggledItemSlots != null && currentlySelectedReservedItemSlot != null && currentlyToggledItemSlots.Contains(currentlySelectedReservedItemSlot));
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "Awake")]
		[HarmonyPrefix]
		public static void InitSession(StartOfRound __instance)
		{
			currentlyToggledItemSlots = new List<ReservedItemSlotData>();
			ReservedPlayerData.allPlayerData.Clear();
			indexInHotbar = 0;
			indexInReservedHotbar = -1;
		}

		public static void ForceToggleReservedHotbar(params ReservedItemSlotData[] reservedItemSlots)
		{
			if (((NetworkBehaviour)localPlayerController).IsOwner && localPlayerController.isPlayerControlled && (!((NetworkBehaviour)localPlayerController).IsServer || localPlayerController.isHostPlayerObject) && HUDPatcher.hasReservedItemSlotsAndEnabled && reservedHotbarSize > 0 && CanSwapHotbars() && reservedItemSlots != null && reservedItemSlots.Length != 0 && !((Object)(object)localPlayerController == (Object)null))
			{
				currentlyToggledItemSlots = new List<ReservedItemSlotData>(reservedItemSlots);
				int num = currentlyToggledItemSlots.First().GetReservedItemSlotIndex() + localPlayerData.reservedHotbarStartIndex;
				bool active = ReservedPlayerData.localPlayerData.IsReservedItemSlot(num);
				if (currentlyToggledItemSlots.Contains(localPlayerData.GetCurrentlySelectedReservedItemSlot()))
				{
					FocusReservedHotbarSlots(active: false);
					return;
				}
				HUDPatcher.UpdateToggledReservedItemSlotsUI();
				FocusReservedHotbarSlots(active, num);
			}
		}

		public static void FocusReservedHotbarSlots(bool active, int forceSlot = -1)
		{
			if (!HUDPatcher.hasReservedItemSlotsAndEnabled || (reservedHotbarSize <= 0 && active) || (ReservedPlayerData.localPlayerData.currentItemSlotIsReserved == active && (forceSlot == -1 || localPlayerData.currentItemSlot == forceSlot)))
			{
				return;
			}
			if (forceSlot != -1)
			{
				active = localPlayerData.IsReservedItemSlot(forceSlot);
			}
			ReservedPlayerData reservedPlayerData = ReservedPlayerData.localPlayerData;
			indexInHotbar = Mathf.Clamp(indexInHotbar, 0, localPlayerController.ItemSlots.Length - 1);
			indexInHotbar = ((!reservedPlayerData.IsReservedItemSlot(indexInHotbar)) ? indexInHotbar : 0);
			indexInReservedHotbar = Mathf.Clamp(indexInReservedHotbar, reservedPlayerData.reservedHotbarStartIndex, reservedPlayerData.reservedHotbarEndIndexExcluded - 1);
			int num = Mathf.Clamp(localPlayerController.currentItemSlot, 0, localPlayerController.ItemSlots.Length);
			int i = num;
			bool flag = active;
			if (flag && (!reservedPlayerData.IsReservedItemSlot(num) || forceSlot != -1))
			{
				indexInHotbar = num;
				indexInHotbar = ((!reservedPlayerData.IsReservedItemSlot(indexInHotbar)) ? indexInHotbar : 0);
				if (forceSlot != -1 && reservedPlayerData.IsReservedItemSlot(forceSlot))
				{
					indexInReservedHotbar = forceSlot;
				}
				i = indexInReservedHotbar;
				if ((Object)(object)localPlayerController.ItemSlots[i] == (Object)null && reservedPlayerData.GetNumHeldReservedItems() > 0)
				{
					for (i = reservedPlayerData.reservedHotbarStartIndex; i < reservedPlayerData.reservedHotbarEndIndexExcluded && !((Object)(object)localPlayerController.ItemSlots[i] != (Object)null); i++)
					{
					}
				}
				Plugin.Log("Focusing reserved hotbar slots. NewIndex: " + i + " OldIndex: " + num + " ReservedStartIndex: " + ReservedPlayerData.localPlayerData.reservedHotbarStartIndex);
			}
			else if (!flag && (ReservedPlayerData.localPlayerData.IsReservedItemSlot(num) || forceSlot != -1))
			{
				indexInReservedHotbar = Mathf.Clamp(num, reservedPlayerData.reservedHotbarStartIndex, reservedPlayerData.reservedHotbarEndIndexExcluded - 1);
				if (forceSlot != -1 && !reservedPlayerData.IsReservedItemSlot(forceSlot))
				{
					indexInHotbar = forceSlot;
				}
				i = indexInHotbar;
				Plugin.Log("Unfocusing reserved hotbar slots. NewIndex: " + i + " OldIndex: " + num + " ReservedStartIndex: " + ReservedPlayerData.localPlayerData.reservedHotbarStartIndex);
			}
			if (i < 0)
			{
				Plugin.LogError("Swapping to hotbar slot: " + i + ". Maybe send these logs to Flip? :)");
			}
			else if (i >= localPlayerController.ItemSlots.Length)
			{
				Plugin.LogError("Swapping to hotbar slot: " + i + " InventorySize: " + localPlayerController.ItemSlots.Length + ". Maybe send these logs to Flip? :)");
			}
			SyncManager.SwapHotbarSlot(i);
			if (localPlayerController.currentItemSlot != i)
			{
				Plugin.LogWarning("OnFocusReservedHotbarSlots - New hotbar index does not match target hotbar index. Tried to swap to index: " + i + " Current index: " + localPlayerController.currentItemSlot + " Tried swapping to reserved hotbar: " + active);
			}
		}

		public static bool CanSwapHotbars()
		{
			if (!HUDPatcher.hasReservedItemSlotsAndEnabled)
			{
				return false;
			}
			if (TooManyEmotes_Compat.Enabled && TooManyEmotes_Compat.IsLocalPlayerPerformingCustomEmote() && !TooManyEmotes_Compat.CanMoveWhileEmoting())
			{
				return false;
			}
			return ReservedPlayerData.localPlayerData.grabbingReservedItemData == null && !localPlayerController.isGrabbingObjectAnimation && !localPlayerController.quickMenuManager.isMenuOpen && !localPlayerController.inSpecialInteractAnimation && !localPlayerData.throwingObject && !localPlayerController.isTypingChat && !localPlayerController.twoHanded && !localPlayerController.activatingItem && !localPlayerController.jetpackControls && !localPlayerController.disablingJetpackControls && !localPlayerController.inTerminalMenu && !localPlayerController.isPlayerDead && !(localPlayerData.timeSinceSwitchingSlots < 0.3f);
		}

		internal static void OnSwapToReservedHotbar()
		{
			if (!localPlayerData.currentItemSlotIsReserved)
			{
				return;
			}
			if (localPlayerData.currentItemSlotIsReserved)
			{
				indexInReservedHotbar = localPlayerController.currentItemSlot;
			}
			ReservedItemSlotData currentlySelectedReservedItemSlot = localPlayerData.GetCurrentlySelectedReservedItemSlot();
			if (isToggledInReservedSlots && currentlyToggledItemSlots != null && !currentlyToggledItemSlots.Contains(currentlySelectedReservedItemSlot))
			{
				currentlyToggledItemSlots = null;
			}
			if (HUDPatcher.reservedItemSlots == null)
			{
				return;
			}
			foreach (Image reservedItemSlot in HUDPatcher.reservedItemSlots)
			{
				CanvasGroup component = ((Component)reservedItemSlot).GetComponent<CanvasGroup>();
				if ((Object)(object)component != (Object)null)
				{
					component.ignoreParentGroups = true;
				}
			}
		}

		internal static void OnSwapToVanillaHotbar()
		{
			if (localPlayerData.currentItemSlotIsReserved)
			{
				return;
			}
			if (!localPlayerData.currentItemSlotIsReserved)
			{
				indexInHotbar = localPlayerController.currentItemSlot;
			}
			currentlyToggledItemSlots = null;
			if (HUDPatcher.reservedItemSlots == null)
			{
				return;
			}
			foreach (Image reservedItemSlot in HUDPatcher.reservedItemSlots)
			{
				CanvasGroup component = ((Component)reservedItemSlot).GetComponent<CanvasGroup>();
				if ((Object)(object)component != (Object)null)
				{
					component.ignoreParentGroups = ConfigSettings.preventReservedItemSlotFade.Value;
				}
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "LateUpdate")]
		[HarmonyPrefix]
		private static void RefocusReservedHotbarAfterAnimation(PlayerControllerB __instance)
		{
			if (HUDPatcher.hasReservedItemSlotsAndEnabled && !((Object)(object)__instance != (Object)(object)localPlayerController) && !Keybinds.pressedToggleKey && Keybinds.holdingModifierKey != ReservedPlayerData.localPlayerData.currentItemSlotIsReserved && !isToggledInReservedSlots && CanSwapHotbars())
			{
				FocusReservedHotbarSlots(Keybinds.holdingModifierKey);
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "UpdateSpecialAnimationValue")]
		[HarmonyPostfix]
		private static void UpdateReservedHotbarAfterAnimation(bool specialAnimation, PlayerControllerB __instance)
		{
			if (HUDPatcher.hasReservedItemSlotsAndEnabled && !((Object)(object)__instance != (Object)(object)localPlayerController) && !specialAnimation && !Keybinds.pressedToggleKey && ReservedPlayerData.localPlayerData.currentItemSlotIsReserved != Keybinds.holdingModifierKey)
			{
				FocusReservedHotbarSlots(Keybinds.holdingModifierKey);
			}
		}
	}
	[HarmonyPatch]
	public static class SessionManager
	{
		internal static List<ReservedItemSlotData> unlockedReservedItemSlots = new List<ReservedItemSlotData>();

		internal static Dictionary<string, ReservedItemSlotData> unlockedReservedItemSlotsDict = new Dictionary<string, ReservedItemSlotData>();

		internal static List<ReservedItemSlotData> pendingUnlockedReservedItemSlots = new List<ReservedItemSlotData>();

		internal static Dictionary<string, ReservedItemSlotData> pendingUnlockedReservedItemSlotsDict = new Dictionary<string, ReservedItemSlotData>();

		private static Dictionary<string, ReservedItemData> allReservedItemData = new Dictionary<string, ReservedItemData>();

		internal static bool gameStarted = false;

		internal static List<ReservedItemSlotData> allUnlockableReservedItemSlots => SyncManager.unlockableReservedItemSlots;

		internal static Dictionary<string, ReservedItemSlotData> allUnlockableReservedItemSlotsDict => SyncManager.unlockableReservedItemSlotsDict;

		public static int numReservedItemSlotsUnlocked => (unlockedReservedItemSlots != null) ? unlockedReservedItemSlots.Count : 0;

		[HarmonyPatch(typeof(StartOfRound), "Awake")]
		[HarmonyPrefix]
		private static void InitSession()
		{
			unlockedReservedItemSlots.Clear();
			unlockedReservedItemSlotsDict.Clear();
			pendingUnlockedReservedItemSlots.Clear();
			pendingUnlockedReservedItemSlotsDict.Clear();
			allReservedItemData.Clear();
			gameStarted = false;
		}

		[HarmonyPatch(typeof(StartOfRound), "ResetPlayersLoadedValueClientRpc")]
		[HarmonyPostfix]
		private static void OnStartGame(StartOfRound __instance, bool landingShip = false)
		{
			if (gameStarted || !NetworkManager.Singleton.IsClient)
			{
				return;
			}
			if (!SyncManager.hostHasMod && SyncManager.canUseModDisabledOnHost)
			{
				Plugin.LogWarning("Starting game while host does not have this mod, and ForceEnableReservedItemSlots is enabled in the config. Unlocking: " + ReservedItemSlotData.allReservedItemSlotData.Count + " slots. THIS MAY NOT BE STABLE");
				SyncManager.isSynced = true;
				SyncManager.enablePurchasingItemSlots = false;
				ReservedPlayerData.localPlayerData.reservedHotbarStartIndex = ReservedPlayerData.localPlayerData.itemSlots.Length;
				foreach (ReservedItemSlotData value in ReservedItemSlotData.allReservedItemSlotData.Values)
				{
					SyncManager.AddReservedItemSlotData(value);
					UnlockReservedItemSlot(value);
				}
				pendingUnlockedReservedItemSlots?.Clear();
				pendingUnlockedReservedItemSlotsDict?.Clear();
				SyncManager.UpdateReservedItemsList();
			}
			gameStarted = true;
		}

		public static void UnlockReservedItemSlot(ReservedItemSlotData itemSlotData)
		{
			if (itemSlotData == null)
			{
				return;
			}
			Plugin.Log("Unlocking reserved item slot: " + itemSlotData.slotName);
			if (!SyncManager.isSynced)
			{
				if (!pendingUnlockedReservedItemSlotsDict.ContainsKey(itemSlotData.slotName))
				{
					pendingUnlockedReservedItemSlotsDict.Add(itemSlotData.slotName, itemSlotData);
					pendingUnlockedReservedItemSlots.Add(itemSlotData);
				}
				return;
			}
			if (!unlockedReservedItemSlotsDict.ContainsKey(itemSlotData.slotName))
			{
				unlockedReservedItemSlotsDict.Add(itemSlotData.slotName, itemSlotData);
				if (!unlockedReservedItemSlots.Contains(itemSlotData))
				{
					int num = -1;
					for (int i = 0; i < unlockedReservedItemSlots.Count; i++)
					{
						if (itemSlotData.slotPriority > unlockedReservedItemSlots[i].slotPriority)
						{
							num = i;
							break;
						}
					}
					if (num == -1)
					{
						num = unlockedReservedItemSlots.Count;
					}
					unlockedReservedItemSlots.Insert(num, itemSlotData);
					foreach (ReservedPlayerData value in ReservedPlayerData.allPlayerData.Values)
					{
						if (unlockedReservedItemSlots.Count == 1)
						{
							value.reservedHotbarStartIndex = value.itemSlots.Length;
						}
						int index = value.reservedHotbarStartIndex + num;
						List<GrabbableObject> list = new List<GrabbableObject>(value.itemSlots);
						list.Insert(index, null);
						value.playerController.ItemSlots = list.ToArray();
						value.hotbarSize = list.Count;
					}
				}
			}
			if (ReservedHotbarManager.indexInReservedHotbar < ReservedPlayerData.localPlayerData.reservedHotbarStartIndex || ReservedHotbarManager.indexInReservedHotbar >= ReservedPlayerData.localPlayerData.reservedHotbarEndIndexExcluded)
			{
				ReservedHotbarManager.indexInReservedHotbar = ReservedPlayerData.localPlayerData.reservedHotbarStartIndex;
			}
			UpdateReservedItemsList();
			HUDPatcher.OnUpdateReservedItemSlots();
		}

		internal static void UnlockAllPendingItemSlots()
		{
			foreach (ReservedItemSlotData pendingUnlockedReservedItemSlot in pendingUnlockedReservedItemSlots)
			{
				UnlockReservedItemSlot(pendingUnlockedReservedItemSlot);
			}
			pendingUnlockedReservedItemSlots.Clear();
			pendingUnlockedReservedItemSlotsDict.Clear();
		}

		public static ReservedItemSlotData GetUnlockedReservedItemSlot(int indexInUnlockedItemSlots)
		{
			return (unlockedReservedItemSlots != null && indexInUnlockedItemSlots >= 0 && indexInUnlockedItemSlots < unlockedReservedItemSlots.Count) ? unlockedReservedItemSlots[indexInUnlockedItemSlots] : null;
		}

		public static ReservedItemSlotData GetUnlockedReservedItemSlot(string itemSlotName)
		{
			if (TryGetUnlockedItemSlotData(itemSlotName, out var itemSlotData))
			{
				return itemSlotData;
			}
			return null;
		}

		public static bool IsItemSlotUnlocked(ReservedItemSlotData itemSlotData)
		{
			return itemSlotData != null && IsItemSlotUnlocked(itemSlotData.slotName);
		}

		public static bool IsItemSlotUnlocked(string itemSlotName)
		{
			return unlockedReservedItemSlotsDict.ContainsKey(itemSlotName);
		}

		internal static void UpdateReservedItemsList()
		{
			if (unlockedReservedItemSlots == null)
			{
				return;
			}
			allReservedItemData.Clear();
			foreach (ReservedItemSlotData unlockedReservedItemSlot in unlockedReservedItemSlots)
			{
				if (unlockedReservedItemSlot.reservedItemData == null)
				{
					continue;
				}
				foreach (ReservedItemData value in unlockedReservedItemSlot.reservedItemData.Values)
				{
					if (!allReservedItemData.ContainsKey(value.itemName))
					{
						allReservedItemData.Add(value.itemName, value);
					}
				}
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "ResetShip")]
		[HarmonyPostfix]
		private static void OnResetShip()
		{
			if (SyncManager.enablePurchasingItemSlots)
			{
				ResetProgressDelayed();
			}
			else if (!SyncManager.hostHasMod && SyncManager.canUseModDisabledOnHost)
			{
				SyncManager.isSynced = false;
				ResetProgressDelayed(force: true);
			}
			gameStarted = false;
		}

		[HarmonyPatch(typeof(GameNetworkManager), "SaveGameValues")]
		[HarmonyPostfix]
		private static void OnSaveGameValues()
		{
			if (NetworkManager.Singleton.IsHost && StartOfRound.Instance.inShipPhase && SyncManager.enablePurchasingItemSlots)
			{
				SaveGameValues();
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "LoadUnlockables")]
		[HarmonyPostfix]
		private static void OnLoadGameValues()
		{
			if (NetworkManager.Singleton.IsServer && SyncManager.isSynced && SyncManager.enablePurchasingItemSlots)
			{
				LoadGameValues();
			}
		}

		internal static void ResetProgress(bool force = false)
		{
			if (!SyncManager.enablePurchasingItemSlots && !force)
			{
				return;
			}
			Plugin.Log("Resetting progress.");
			foreach (ReservedPlayerData value in ReservedPlayerData.allPlayerData.Values)
			{
				GrabbableObject[] itemSlots = value.playerController.ItemSlots;
				List<GrabbableObject> list = new List<GrabbableObject>();
				for (int i = 0; i < itemSlots.Length; i++)
				{
					if (i < value.reservedHotbarStartIndex || i >= value.reservedHotbarEndIndexExcluded)
					{
						list.Add(itemSlots[i]);
					}
				}
				value.playerController.ItemSlots = list.ToArray();
			}
			unlockedReservedItemSlots?.Clear();
			unlockedReservedItemSlotsDict?.Clear();
			pendingUnlockedReservedItemSlots?.Clear();
			pendingUnlockedReservedItemSlotsDict?.Clear();
			List<Image> list2 = new List<Image>();
			List<Image> list3 = new List<Image>();
			for (int j = 0; j < HUDManager.Instance.itemSlotIconFrames.Length; j++)
			{
				Image val = HUDManager.Instance.itemSlotIconFrames[j];
				Image item = HUDManager.Instance.itemSlotIcons[j];
				if (!HUDPatcher.reservedItemSlots.Contains(val))
				{
					list2.Add(val);
					list3.Add(item);
				}
				else
				{
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
			}
			HUDPatcher.reservedItemSlots.Clear();
			HUDManager.Instance.itemSlotIconFrames = list2.ToArray();
			HUDManager.Instance.itemSlotIcons = list3.ToArray();
			foreach (ReservedPlayerData value2 in ReservedPlayerData.allPlayerData.Values)
			{
				if (value2.playerController.currentItemSlot < 0 || value2.playerController.currentItemSlot >= value2.playerController.ItemSlots.Length)
				{
					PlayerPatcher.SwitchToItemSlot(value2.playerController, 0);
				}
				value2.hotbarSize = value2.itemSlots.Length;
				value2.reservedHotbarStartIndex = value2.hotbarSize;
			}
			foreach (ReservedItemSlotData allUnlockableReservedItemSlot in allUnlockableReservedItemSlots)
			{
				if (allUnlockableReservedItemSlot.purchasePrice <= 0)
				{
					UnlockReservedItemSlot(allUnlockableReservedItemSlot);
				}
			}
			if (SyncManager.hostHasMod)
			{
			}
			HUDPatcher.OnUpdateReservedItemSlots();
			if (NetworkManager.Singleton.IsServer)
			{
				ES3.DeleteKey("ReservedItemSlots.UnlockedItemSlots", GameNetworkManager.Instance.currentSaveFileName);
			}
		}

		internal static void ResetProgressDelayed(bool force = false)
		{
			((MonoBehaviour)StartOfRound.Instance).StartCoroutine(Reset());
			IEnumerator Reset()
			{
				yield return null;
				ResetProgress(force);
			}
		}

		internal static void SaveGameValues()
		{
			if (!NetworkManager.Singleton.IsServer || unlockedReservedItemSlots == null)
			{
				return;
			}
			List<string> list = new List<string>();
			foreach (ReservedItemSlotData unlockedReservedItemSlot in unlockedReservedItemSlots)
			{
				if (!list.Contains(unlockedReservedItemSlot.slotName))
				{
					list.Add(unlockedReservedItemSlot.slotName);
				}
			}
			Plugin.LogWarning("Saving " + list.Count + " unlocked reserved item slots.");
			string[] array = list.ToArray();
			ES3.Save<string[]>("ReservedItemSlots.UnlockedItemSlots", array, GameNetworkManager.Instance.currentSaveFileName);
		}

		internal static void LoadGameValues()
		{
			if (!NetworkManager.Singleton.IsServer || SyncManager.unlockableReservedItemSlotsDict == null)
			{
				return;
			}
			string[] array = ES3.Load<string[]>("ReservedItemSlots.UnlockedItemSlots", GameNetworkManager.Instance.currentSaveFileName, new string[0]);
			Plugin.LogWarning("Loading " + array.Length + " unlocked reserved item slots.");
			int num = 0;
			string[] array2 = array;
			foreach (string key in array2)
			{
				if (SyncManager.unlockableReservedItemSlotsDict.TryGetValue(key, out var value))
				{
					num++;
					UnlockReservedItemSlot(value);
					SyncManager.SendUnlockItemSlotToClients(value.slotId);
				}
			}
			Plugin.Log("Loaded " + num + " unlocked reserved items.");
		}

		public static bool IsReservedItem(GrabbableObject grabbableObject)
		{
			string itemName = ItemNameMap.GetItemName(grabbableObject);
			return IsReservedItem(itemName) || ((Object)(object)grabbableObject?.itemProperties != (Object)null && IsReservedItem(grabbableObject.itemProperties.itemName));
		}

		public static bool IsReservedItem(string itemName)
		{
			return allReservedItemData.ContainsKey(itemName);
		}

		public static bool TryGetUnlockedItemSlotData(string itemSlotName, out ReservedItemSlotData itemSlotData)
		{
			itemSlotData = null;
			unlockedReservedItemSlotsDict.TryGetValue(itemSlotName, out itemSlotData);
			return itemSlotData != null;
		}

		public static bool TryGetUnlockedItemData(GrabbableObject item, out ReservedItemData itemData)
		{
			itemData = null;
			string itemName = ItemNameMap.GetItemName(item);
			return TryGetUnlockedItemData(itemName, out itemData) || ((Object)(object)item?.itemProperties != (Object)null && TryGetUnlockedItemData(item.itemProperties.itemName, out itemData));
		}

		public static bool TryGetUnlockedItemData(string itemName, out ReservedItemData itemData)
		{
			itemData = null;
			return allReservedItemData.TryGetValue(itemName, out itemData);
		}
	}
}
namespace ReservedItemSlotCore.Patches
{
	[HarmonyPatch]
	internal static class DropReservedItemPatcher
	{
		private static HashSet<PlayerControllerB> playersDiscardingItems = new HashSet<PlayerControllerB>();

		private static float timeLoggedPreventedScroll = 0f;

		private static PlayerControllerB localPlayerController => StartOfRound.Instance?.localPlayerController;

		[HarmonyPatch(typeof(PlayerControllerB), "SetObjectAsNoLongerHeld")]
		[HarmonyPostfix]
		private static void OnSetObjectNoLongerHeld(bool droppedInElevator, bool droppedInShipRoom, Vector3 targetFloorPosition, GrabbableObject dropObject, PlayerControllerB __instance)
		{
			OnDiscardItem(__instance);
		}

		[HarmonyPatch(typeof(PlayerControllerB), "PlaceGrabbableObject")]
		[HarmonyPostfix]
		private static void OnPlaceGrabbableObject(Transform parentObject, Vector3 positionOffset, bool matchRotationOfParent, GrabbableObject placeObject, PlayerControllerB __instance)
		{
			OnDiscardItem(__instance);
		}

		[HarmonyPatch(typeof(PlayerControllerB), "DestroyItemInSlot")]
		[HarmonyPostfix]
		private static void OnDestroyItem(int itemSlot, PlayerControllerB __instance)
		{
			if ((Object)(object)__instance == (Object)(object)localPlayerController && itemSlot >= ReservedPlayerData.localPlayerData.reservedHotbarStartIndex && itemSlot < ReservedPlayerData.localPlayerData.reservedHotbarEndIndexExcluded)
			{
				HUDPatcher.UpdateUI();
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "DespawnHeldObjectOnClient")]
		[HarmonyPostfix]
		private static void OnDespawnItem(PlayerControllerB __instance)
		{
			if ((Object)(object)__instance == (Object)(object)localPlayerController)
			{
				HUDPatcher.UpdateUI();
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "DropAllHeldItems")]
		[HarmonyPostfix]
		private static void OnDropAllHeldItems(PlayerControllerB __instance)
		{
			if ((Object)(object)__instance == (Object)(object)localPlayerController)
			{
				HUDPatcher.UpdateUI();
			}
		}

		private static void OnDiscardItem(PlayerControllerB playerController)
		{
			if (!((Object)(object)playerController != (Object)null) || playersDiscardingItems.Contains(playerController) || !ReservedPlayerData.allPlayerData.TryGetValue(playerController, out var value) || !value.currentItemSlotIsReserved || !((Object)(object)value.currentlySelectedItem == (Object)null))
			{
				return;
			}
			if (value.GetNumHeldReservedItems() > 0)
			{
				int num = value.CallGetNextItemSlot(forward: true);
				if (!value.IsReservedItemSlot(num) && !value.IsReservedItemSlot(ReservedHotbarManager.indexInHotbar))
				{
					num = ReservedHotbarManager.indexInHotbar;
				}
				playersDiscardingItems.Add(playerController);
				((MonoBehaviour)playerController).StartCoroutine(SwitchToItemSlotAfterDelay(playerController, num));
			}
			if ((Object)(object)playerController == (Object)(object)localPlayerController)
			{
				HUDPatcher.UpdateUI();
			}
		}

		private static IEnumerator SwitchToItemSlotAfterDelay(PlayerControllerB playerController, int slot)
		{
			float time = Time.time;
			if ((Object)(object)playerController == (Object)(object)localPlayerController)
			{
				yield return (object)new WaitUntil((Func<bool>)(() => (Object)(object)playerController.currentlyHeldObjectServer == (Object)null || Time.time - time >= 5f));
			}
			yield return (object)new WaitForEndOfFrame();
			playersDiscardingItems.Remove(playerController);
			if (playerController.currentItemSlot != slot && Time.time - time < 3f && ReservedPlayerData.allPlayerData.TryGetValue(playerController, out var playerData))
			{
				playerData.CallSwitchToItemSlot(slot);
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "ScrollMouse_performed")]
		[HarmonyPrefix]
		private static bool PreventItemSwappingDroppingItem(CallbackContext context, PlayerControllerB __instance)
		{
			if ((Object)(object)__instance == (Object)(object)localPlayerController && playersDiscardingItems.Contains(__instance))
			{
				float time = Time.time;
				if (ConfigSettings.verboseLogs.Value && time - timeLoggedPreventedScroll > 1f)
				{
					timeLoggedPreventedScroll = time;
					Plugin.LogWarning("[VERBOSE] Prevented item swap. Player is currently discarding an item? This should be fine, unless these logs are spamming.");
				}
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch]
	public static class HUDPatcher
	{
		private static bool usingController = false;

		private static float itemSlotWidth;

		internal static float itemSlotSpacing;

		private static float defaultItemSlotPosX;

		private static float defaultItemSlotPosY;

		private static float defaultItemSlotSpacing;

		private static Vector2 defaultItemSlotSize;

		private static Vector2 defaultItemIconSize;

		private static TextMeshProUGUI hotkeyTooltip;

		public static List<Image> reservedItemSlots = new List<Image>();

		public static HashSet<ReservedItemSlotData> toggledReservedItemSlots = new HashSet<ReservedItemSlotData>();

		private static bool lerpToggledItemSlotFrames = false;

		private static float largestPositionDifference = 0f;

		private static bool currentApplyHotbarPlusSize;

		private static bool currentHideEmptySlots;

		public static PlayerControllerB localPlayerController => StartOfRound.Instance?.localPlayerController;

		public static ReservedPlayerData localPlayerData => ReservedPlayerData.localPlayerData;

		public static bool localPlayerUsingController => (Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.localPlayerUsingController;

		private static float currentItemSlotScale => itemSlotWidth / defaultItemSlotSize.x;

		public static bool hasReservedItemSlotsAndEnabled => reservedItemSlots != null && reservedItemSlots.Count > 0 && ((Component)reservedItemSlots[0]).gameObject.activeSelf && ((Behaviour)reservedItemSlots[0]).enabled;

		[HarmonyPatch(typeof(HUDManager), "Awake")]
		[HarmonyPostfix]
		public static void Initialize(HUDManager __instance)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//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_00b7: Unknown result type (might be due to invalid IL or missing references)
			CanvasScaler componentInParent = ((Component)__instance.itemSlotIconFrames[0]).GetComponentInParent<CanvasScaler>();
			AspectRatioFitter componentInParent2 = ((Component)__instance.itemSlotIconFrames[0]).GetComponentInParent<AspectRatioFitter>();
			itemSlotWidth = ((Graphic)__instance.itemSlotIconFrames[0]).rectTransform.sizeDelta.x;
			itemSlotSpacing = 1.125f * itemSlotWidth;
			defaultItemSlotPosX = componentInParent.referenceResolution.x / 2f / componentInParent2.aspectRatio - itemSlotWidth / 4f;
			defaultItemSlotSpacing = itemSlotSpacing;
			defaultItemSlotSize = ((Graphic)__instance.itemSlotIconFrames[0]).rectTransform.sizeDelta;
			defaultItemIconSize = ((Graphic)__instance.itemSlotIcons[0]).rectTransform.sizeDelta;
			defaultItemSlotPosY = ((Graphic)__instance.itemSlotIconFrames[0]).rectTransform.anchoredPosition.y;
			reservedItemSlots.Clear();
		}

		[HarmonyPatch(typeof(StartOfRound), "Update")]
		[HarmonyPrefix]
		public static void UpdateUsingController(StartOfRound __instance)
		{
			if (!((Object)(object)__instance.localPlayerController == (Object)null) && !((Object)(object)hotkeyTooltip == (Object)null) && ((Component)hotkeyTooltip).gameObject.activeSelf && ((Behaviour)hotkeyTooltip).enabled)
			{
				if (__instance.localPlayerUsingController != usingController)
				{
					usingController = __instance.localPlayerUsingController;
					UpdateHotkeyTooltipText();
				}
				LerpItemSlotFrames();
			}
		}

		private static void LerpItemSlotFrames()
		{
			//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_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			if (!lerpToggledItemSlotFrames)
			{
				return;
			}
			if (largestPositionDifference < 2f && largestPositionDifference != -1f)
			{
				lerpToggledItemSlotFrames = false;
			}
			for (int i = 0; i < SessionManager.numReservedItemSlotsUnlocked; i++)
			{
				ReservedItemSlotData unlockedReservedItemSlot = SessionManager.GetUnlockedReservedItemSlot(i);
				Image val = HUDManager.Instance.itemSlotIconFrames[ReservedPlayerData.localPlayerData.reservedHotbarStartIndex + i];
				bool flag = unlockedReservedItemSlot.slotPriority >= 0 || !ConfigSettings.displayNegativePrioritySlotsLeftSideOfScreen.Value;
				Vector2 anchoredPosition = ((Graphic)val).rectTransform.anchoredPosition;
				anchoredPosition.x = (defaultItemSlotPosX + (defaultItemSlotSize.x - itemSlotWidth) / 2f) * (float)(flag ? 1 : (-1));
				if (ReservedHotbarManager.isToggledInReservedSlots && ReservedHotbarManager.currentlyToggledItemSlots != null && ReservedHotbarManager.currentlyToggledItemSlots.Contains(unlockedReservedItemSlot))
				{
					anchoredPosition.x += itemSlotWidth / 2f * (float)((!flag) ? 1 : (-1));
				}
				float num = Mathf.Abs(anchoredPosition.x - ((Graphic)val).rectTransform.anchoredPosition.x);
				largestPositionDifference = Mathf.Max(largestPositionDifference, num);
				if (lerpToggledItemSlotFrames)
				{
					((Graphic)val).rectTransform.anchoredPosition = Vector2.Lerp(((Graphic)val).rectTransform.anchoredPosition, anchoredPosition, Time.deltaTime * 10f);
				}
				else
				{
					((Graphic)val).rectTransform.anchoredPosition = anchoredPosition;
				}
			}
		}

		public static void OnUpdateReservedItemSlots()
		{
			//IL_00b2: 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_00ee: 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)
			if (reservedItemSlots == null || SessionManager.numReservedItemSlotsUnlocked <= 0 || reservedItemSlots.Count == SessionManager.numReservedItemSlotsUnlocked)
			{
				return;
			}
			List<Image> list = new List<Image>(HUDManager.Instance.itemSlotIconFrames);
			List<Image> list2 = new List<Image>(HUDManager.Instance.itemSlotIcons);
			for (int i = reservedItemSlots.Count; i < SessionManager.numReservedItemSlotsUnlocked; i++)
			{
				GameObject val = Object.Instantiate<GameObject>(((Component)list[0]).gameObject, ((Component)list[0]).transform.parent);
				Image component = val.GetComponent<Image>();
				Image component2 = ((Component)((Component)component).transform.GetChild(0)).GetComponent<Image>();
				((Component)component).transform.localScale = ((Component)list[0]).transform.localScale;
				((Transform)((Graphic)component).rectTransform).eulerAngles = ((Transform)((Graphic)list[0]).rectTransform).eulerAngles;
				((Transform)((Graphic)component2).rectTransform).eulerAngles = ((Transform)((Graphic)list2[0]).rectTransform).eulerAngles;
				CanvasGroup val2 = ((Component)component).gameObject.AddComponent<CanvasGroup>();
				val2.ignoreParentGroups = ConfigSettings.preventReservedItemSlotFade.Value;
				val2.alpha = 1f;
				component.fillMethod = list[0].fillMethod;
				component.sprite = list[0].sprite;
				((Graphic)component).material = ((Graphic)list[0]).material;
				if (Plugin.IsModLoaded("xuxiaolan.hotbarrd"))
				{
					component.overrideSprite = list[0].overrideSprite;
				}
				int index = ReservedPlayerData.localPlayerData.reservedHotbarStartIndex + reservedItemSlots.Count;
				list.Insert(index, component);
				list2.Insert(index, component2);
				reservedItemSlots.Add(component);
			}
			HUDManager.Instance.itemSlotIconFrames = list.ToArray();
			HUDManager.Instance.itemSlotIcons = list2.ToArray();
			UpdateUI();
		}

		public static void UpdateUI()
		{
			//IL_00ab: 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)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0401: Unknown result type (might be due to invalid IL or missing references)
			//IL_0416: Unknown result type (might be due to invalid IL or missing references)
			//IL_0423: Unknown result type (might be due to invalid IL or missing references)
			//IL_042d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0441: Unknown result type (might be due to invalid IL or missing references)
			//IL_0469: Unknown result type (might be due to invalid IL or missing references)
			//IL_049b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0301: Unknown result type (might be due to invalid IL or missing references)
			if (reservedItemSlots.Count != SessionManager.numReservedItemSlotsUnlocked)
			{
				Plugin.LogError("Called UpdateUI with mismatched unlocked reserved item slots and reserved item slot hud elements.");
				return;
			}
			int num = 0;
			int num2 = 0;
			RectTransform val = null;
			Vector2 anchoredPosition = default(Vector2);
			for (int i = 0; i < SessionManager.numReservedItemSlotsUnlocked; i++)
			{
				ReservedItemSlotData unlockedReservedItemSlot = SessionManager.GetUnlockedReservedItemSlot(i);
				int num3 = Array.IndexOf(HUDManager.Instance.itemSlotIconFrames, reservedItemSlots[i]);
				Image val2 = HUDManager.Instance.itemSlotIconFrames[ReservedPlayerData.localPlayerData.reservedHotbarStartIndex + i];
				Image val3 = HUDManager.Instance.itemSlotIcons[ReservedPlayerData.localPlayerData.reservedHotbarStartIndex + i];
				((Graphic)val2).rectTransform.sizeDelta = ((Graphic)HUDManager.Instance.itemSlotIconFrames[0]).rectTransform.sizeDelta;
				((Graphic)val3).rectTransform.sizeDelta = ((Graphic)HUDManager.Instance.itemSlotIcons[0]).rectTransform.sizeDelta;
				if (HotbarPlus_Compat.Enabled && !ConfigSettings.applyHotbarPlusItemSlotSize.Value)
				{
					((Graphic)val2).rectTransform.sizeDelta = defaultItemSlotSize;
					((Graphic)val3).rectTransform.sizeDelta = defaultItemIconSize;
				}
				itemSlotWidth = ((Graphic)val2).rectTransform.sizeDelta.x;
				itemSlotSpacing = defaultItemSlotSpacing * currentItemSlotScale;
				GrabbableObject reservedItem = ReservedPlayerData.localPlayerData.GetReservedItem(unlockedReservedItemSlot);
				((Object)val2).name = "Slot" + i + " [ReservedItemSlot] (" + unlockedReservedItemSlot.slotName + ")";
				((Vector2)(ref anchoredPosition))..ctor(defaultItemSlotPosX, defaultItemSlotPosY);
				if (unlockedReservedItemSlot.slotPriority >= 0 || !ConfigSettings.displayNegativePrioritySlotsLeftSideOfScreen.Value)
				{
					anchoredPosition.x = defaultItemSlotPosX + (defaultItemSlotSize.x - itemSlotWidth) / 2f;
					anchoredPosition.y = defaultItemSlotPosY + 36f * ((itemSlotWidth / defaultItemSlotSize.x - 1f) / 2f) + itemSlotSpacing * (float)num;
					if (!ConfigSettings.hideEmptyReservedItemSlots.Value || (Object)(object)reservedItem != (Object)null)
					{
						if (!Object.op_Implicit((Object)(object)val))
						{
							val = ((Graphic)val2).rectTransform;
						}
						num++;
					}
					else
					{
						anchoredPosition.y = -1000f;
					}
				}
				else
				{
					anchoredPosition.x = 0f - defaultItemSlotPosX - (defaultItemSlotSize.x - itemSlotWidth) / 2f;
					anchoredPosition.y = defaultItemSlotPosY + 36f * ((itemSlotWidth / defaultItemSlotSize.x - 1f) / 2f) + itemSlotSpacing * (float)num2;
					if (!ConfigSettings.hideEmptyReservedItemSlots.Value || (Object)(object)reservedItem != (Object)null)
					{
						num2++;
					}
					else
					{
						anchoredPosition.y = -1000f;
					}
				}
				((Graphic)val2).rectTransform.anchoredPosition = anchoredPosition;
				if ((Object)(object)reservedItem != (Object)null)
				{
					((Behaviour)val3).enabled = true;
					val3.sprite = reservedItem.itemProperties.itemIcon;
				}
				else
				{
					((Behaviour)val3).enabled = false;
					val3.sprite = null;
				}
			}
			if (SessionManager.numReservedItemSlotsUnlocked > 0 && !ConfigSettings.hideFocusHotbarTooltip.Value)
			{
				if ((Object)(object)hotkeyTooltip == (Object)null)
				{
					hotkeyTooltip = new GameObject("ReservedItemSlotTooltip", new Type[2]
					{
						typeof(RectTransform),
						typeof(TextMeshProUGUI)
					}).GetComponent<TextMeshProUGUI>();
				}
				RectTransform rectTransform = ((TMP_Text)hotkeyTooltip).rectTransform;
				((Transform)rectTransform).parent = (Transform)(object)val;
				if (Object.op_Implicit((Object)(object)val))
				{
					((Transform)rectTransform).localScale = Vector3.one;
					rectTransform.sizeDelta = new Vector2(val.sizeDelta.x * 2f, 10f);
					rectTransform.pivot = Vector2.one / 2f;
					rectTransform.anchoredPosition3D = new Vector3(0f, 0f - rectTransform.sizeDelta.x / 2f - itemSlotWidth / 2f - 5f, 0f);
					((TMP_Text)hotkeyTooltip).font = ((TMP_Text)HUDManager.Instance.controlTipLines[0]).font;
					((TMP_Text)hotkeyTooltip).fontSize = 7f * (val.sizeDelta.x / defaultItemSlotSize.x);
					((TMP_Text)hotkeyTooltip).alignment = (TextAlignmentOptions)4100;
					UpdateHotkeyTooltipText();
				}
				else
				{
					((Transform)rectTransform).localScale = Vector3.zero;
				}
			}
			currentApplyHotbarPlusSize = ConfigSettings.applyHotbarPlusItemSlotSize.Value;
			currentHideEmptySlots = ConfigSettings.hideEmptyReservedItemSlots.Value;
		}

		public static void UpdateHotkeyTooltipText()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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_0070: 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_007f: 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_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: 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_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)localPlayerController == (Object)null || (Object)(object)hotkeyTooltip == (Object)null || Keybinds.FocusReservedHotbarAction == null)
			{
				return;
			}
			int num = (localPlayerUsingController ? 1 : 0);
			string text = "";
			string text2 = "";
			InputBinding val;
			if (num >= 0 && num < Keybinds.FocusReservedHotbarAction.bindings.Count)
			{
				val = Keybinds.FocusReservedHotbarAction.bindings[num];
				text = KeybindDisplayNames.GetKeybindDisplayName(((InputBinding)(ref val)).effectivePath);
			}
			else
			{
				Plugin.LogError("Failed to update FocusReservedHotbar keybind tooltip. Using controller: " + localPlayerUsingController + " NumFocusReservedHotbarActionBindings: " + Keybinds.FocusReservedHotbarAction.bindings.Count);
			}
			if (num >= 0 && num < Keybinds.ToggleFocusReservedHotbarAction.bindings.Count)
			{
				val = Keybinds.ToggleFocusReservedHotbarAction.bindings[num];
				text2 = KeybindDisplayNames.GetKeybindDisplayName(((InputBinding)(ref val)).effectivePath);
			}
			else
			{
				Plugin.LogError("Failed to update ToggleFocusReservedHotbar keybind tooltip. Using controller: " + localPlayerUsingController + " NumToggleFocusReservedHotbarActionBindings: " + Keybinds.ToggleFocusReservedHotbarAction.bindings.Count);
			}
			((TMP_Text)hotkeyTooltip).text = "";
			if (text != "")
			{
				((TMP_Text)hotkeyTooltip).text = $"Hold: [{text}]";
			}
			if (text2 != "" && text2 != text)
			{
				if (((TMP_Text)hotkeyTooltip).text != "")
				{
					TextMeshProUGUI obj = hotkeyTooltip;
					((TMP_Text)obj).text = ((TMP_Text)obj).text + "\n";
				}
				TextMeshProUGUI obj2 = hotkeyTooltip;
				((TMP_Text)obj2).text = ((TMP_Text)obj2).text + $"Toggle: [{text2}]";
			}
		}

		public static void UpdateToggledReservedItemSlotsUI()
		{
			if (ReservedHotbarManager.currentlyToggledItemSlots != null)
			{
				toggledReservedItemSlots = new HashSet<ReservedItemSlotData>(ReservedHotbarManager.currentlyToggledItemSlots);
			}
			else
			{
				toggledReservedItemSlots.Clear();
			}
			lerpToggledItemSlotFrames = true;
			largestPositionDifference = -1f;
		}

		private static float GetCurrentItemSlotSpacing()
		{
			//IL_005d: 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)
			try
			{
				Image val = HUDManager.Instance.itemSlotIconFrames[0];
				Image val2 = HUDManager.Instance.itemSlotIconFrames[1];
				if (((Object)val).name.ToLower().Contains("reserved") || ((Object)val2).name.ToLower().Contains("reserved"))
				{
					return defaultItemSlotSpacing;
				}
				return Mathf.Abs(((Graphic)val2).rectTransform.anchoredPosition.x - ((Graphic)val).rectTransform.anchoredPosition.x);
			}
			catch
			{
			}
			return defaultItemSlotSpacing;
		}

		[HarmonyPatch(typeof(QuickMenuManager), "CloseQuickMenu")]
		[HarmonyPostfix]
		public static void OnCloseQuickMenu()
		{
			if (HotbarPlus_Compat.Enabled || currentHideEmptySlots != ConfigSettings.hideEmptyReservedItemSlots.Value)
			{
				UpdateUI();
			}
		}
	}
	[HarmonyPatch]
	internal class MaskedEnemyPatcher
	{
		[HarmonyPatch(typeof(MaskedPlayerEnemy), "Awake")]
		[HarmonyPrefix]
		public static void InitMaskedEnemy(MaskedPlayerEnemy __instance)
		{
			if (ConfigSettings.showReservedItemsHolsteredMaskedEnemy.Value && !MaskedEnemyData.allMaskedEnemyData.ContainsKey(__instance))
			{
				MaskedEnemyData.allMaskedEnemyData.Add(__instance, new MaskedEnemyData(__instance));
			}
		}

		[HarmonyPatch(typeof(MaskedPlayerEnemy), "OnDestroy")]
		[HarmonyPrefix]
		public static void OnDestroy(MaskedPlayerEnemy __instance)
		{
			if (MaskedEnemyData.allMaskedEnemyData.TryGetValue(__instance, out var value))
			{
				value.DestroyEquippedItems();
				MaskedEnemyData.allMaskedEnemyData.Remove(__instance);
			}
		}

		[HarmonyPatch(typeof(MaskedPlayerEnemy), "Update")]
		[HarmonyPostfix]
		public static void Update(MaskedPlayerEnemy __instance)
		{
			if (ConfigSettings.showReservedItemsHolsteredMaskedEnemy.Value && MaskedEnemyData.allMaskedEnemyData.TryGetValue(__instance, out var value) && (Object)(object)value.originallyMimickingPlayer == (Object)null && (Object)(object)value.maskedEnemy.mimickingPlayer != (Object)null)
			{
				AddReservedItemsToMaskedEnemy(__instance);
			}
		}

		public static void AddReservedItemsToMaskedEnemy(MaskedPlayerEnemy maskedEnemy)
		{
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			if (!ConfigSettings.showReservedItemsHolsteredMaskedEnemy.Value || !MaskedEnemyData.allMaskedEnemyData.TryGetValue(maskedEnemy, out var value))
			{
				return;
			}
			value.originallyMimickingPlayer = value.maskedEnemy.mimickingPlayer;
			if (!ReservedPlayerData.allPlayerData.TryGetValue(value.originallyMimickingPlayer, out var value2))
			{
				Plugin.LogWarning("Failed to mimic player's equipped reserved items. Could not retrieve player data from: " + value.originallyMimickingPlayer.playerUsername);
				return;
			}
			for (int i = value2.reservedHotbarStartIndex; i < Mathf.Min(value2.reservedHotbarEndIndexExcluded, value2.playerController.ItemSlots.Length); i++)
			{
				GrabbableObject val = value2.playerController.ItemSlots[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				int num = i - value2.reservedHotbarStartIndex;
				if (num < 0 || num >= SessionManager.unlockedReservedItemSlots.Count)
				{
					Plugin.LogWarning("Failed to add reserved item to MaskedEnemy. Could not get ReservedItemSlot at index: " + num + " Item: " + val.itemProperties.itemName + " SlotIndexInInventory: " + i + " ReservedHotbarStartIndex: " + value2.reservedHotbarStartIndex);
					continue;
				}
				ReservedItemSlotData reservedItemSlotData = SessionManager.unlockedReservedItemSlots[num];
				ReservedItemData reservedItemData = reservedItemSlotData.GetReservedItemData(val);
				if (reservedItemData.holsteredParentBone == PlayerBone.None)
				{
					continue;
				}
				Transform bone = value.boneMap.GetBone(reservedItemData.holsteredParentBone);
				if ((Object)(object)bone == (Object)null)
				{
					Plugin.LogWarning("Failed to get bone from masked enemy: " + reservedItemData.holsteredParentBone);
					continue;
				}
				GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, bone);
				val2.transform.localEulerAngles = reservedItemData.holsteredRotationOffset;
				val2.transform.localPosition = reservedItemData.holsteredPositionOffset;
				val2.transform.localScale = ((Component)val).transform.localScale;
				val2.layer = 6;
				MeshRenderer[] componentsInChildren = val2.GetComponentsInChildren<MeshRenderer>();
				foreach (MeshRenderer val3 in componentsInChildren)
				{
					if (!((Object)val3).name.Contains("ScanNode") && !((Component)val3).gameObject.CompareTag("DoNotSet") && !((Component)val3).gameObject.CompareTag("InteractTrigger"))
					{
						((Component)val3).gameObject.layer = 6;
					}
				}
				if (val is FlashlightItem)
				{
					Light[] componentsInChildren2 = val2.GetComponentsInChildren<Light>();
					foreach (Light val4 in componentsInChildren2)
					{
						((Behaviour)val4).enabled = false;
					}
				}
				else
				{
					Light[] componentsInChildren3 = val2.GetComponentsInChildren<Light>();
					foreach (Light val5 in componentsInChildren3)
					{
						((Behaviour)val5).enabled = true;
					}
				}
				GrabbableObject componentInChildren = val2.GetComponentInChildren<GrabbableObject>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.playerHeldBy = null;
					FlashlightItem val6 = (FlashlightItem)(object)((componentInChildren is FlashlightItem) ? componentInChildren : null);
					if ((Object)(object)val6 != (Object)null)
					{
						((Behaviour)val6.flashlightBulb).enabled = true;
						((Behaviour)val6.flashlightBulbGlow).enabled = true;
						((Renderer)val6.flashlightMesh).sharedMaterials[1] = val6.bulbLight;
					}
					ReservedItemsPatcher.ForceEnableItemMesh(componentInChildren, enabled: true);
					componentInChildren.EnablePhysics(false);
				}
				Object.DestroyImmediate((Object)(object)val2.GetComponentInChildren<NetworkObject>());
				Collider[] componentsInChildren4 = val2.GetComponentsInChildren<Collider>();
				foreach (Collider val7 in componentsInChildren4)
				{
					Object.DestroyImmediate((Object)(object)val7);
				}
				MonoBehaviour[] componentsInChildren5 = val2.GetComponentsInChildren<MonoBehaviour>();
				foreach (MonoBehaviour val8 in componentsInChildren5)
				{
					Object.DestroyImmediate((Object)(object)val8);
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class MouseScrollPatcher
	{
		private static bool scrollingItemSlots;

		private static float timeLoggedPreventedScroll;

		public static PlayerControllerB localPlayerController => StartOfRound.Instance?.localPlayerController;

		[HarmonyPatch(typeof(PlayerControllerB), "NextItemSlot")]
		[HarmonyPrefix]
		public static void CorrectReservedScrollDirectionNextItemSlot(ref bool forward)
		{
			if (Keybinds.scrollingReservedHotbar)
			{
				forward = Keybinds.RawScrollAction.ReadValue<float>() > 0f;
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "SwitchItemSlotsServerRpc")]
		[HarmonyPrefix]
		public static void CorrectReservedScrollDirectionServerRpc(ref bool forward)
		{
			if (Keybinds.scrollingReservedHotbar)
			{
				forward = Keybinds.RawScrollAction.ReadValue<float>() > 0f;
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "ScrollMouse_performed")]
		[HarmonyPrefix]
		public static bool PreventInvertedScrollingReservedHotbar(CallbackContext context)
		{
			if (StartOfRound.Instance.localPlayerUsingController || SessionManager.numReservedItemSlotsUnlocked <= 0 || HUDPatcher.reservedItemSlots == null || localPlayerController.inTerminalMenu)
			{
				return true;
			}
			if (ReservedPlayerData.localPlayerData.currentItemSlotIsReserved)
			{
				if (!HUDPatcher.hasReservedItemSlotsAndEnabled)
				{
					return true;
				}
				float time = Time.time;
				if (!Keybinds.scrollingReservedHotbar)
				{
					return false;
				}
				if (ReservedPlayerData.localPlayerData.GetNumHeldReservedItems() == 1 && (Object)(object)ReservedPlayerData.localPlayerData.currentlySelectedItem != (Object)null && !ReservedHotbarManager.isToggledInReservedSlots)
				{
					if (ConfigSettings.verboseLogs.Value && time - timeLoggedPreventedScroll > 1f)
					{
						timeLoggedPreventedScroll = time;
					}
					return false;
				}
			}
			return true;
		}

		[HarmonyPatch(typeof(PlayerControllerB), "ScrollMouse_performed")]
		[HarmonyPostfix]
		public static void ScrollReservedItemSlots(CallbackContext context)
		{
			scrollingItemSlots = false;
		}
	}
	[HarmonyPatch]
	internal static class PlayerPatcher
	{
		private static int INTERACTABLE_OBJECT_MASK = 0;

		public static int vanillaHotbarSize = 4;

		private static bool initialized = false;

		public static PlayerControllerB localPlayerController => StartOfRound.Instance?.localPlayerController;

		public static Dictionary<PlayerControllerB, ReservedPlayerData> allPlayerData => ReservedPlayerData.allPlayerData;

		public static ReservedPlayerData localPlayerData => ReservedPlayerData.localPlayerData;

		public static int reservedHotbarSize => SessionManager.numReservedItemSlotsUnlocked;

		[HarmonyPatch(typeof(StartOfRound), "Awake")]
		[HarmonyPrefix]
		private static void InitSession(StartOfRound __instance)
		{
			initialized = false;
			vanillaHotbarSize = 4;
			ReservedPlayerData.allPlayerData?.Clear();
		}

		[HarmonyPatch(typeof(PlayerControllerB), "Awake")]
		[HarmonyPostfix]
		private static void InitializePlayerController(PlayerControllerB __instance)
		{
			if (!initialized)
			{
				vanillaHotbarSize = __instance.ItemSlots.Length;
				INTERACTABLE_OBJECT_MASK = (int)Traverse.Create((object)__instance).Field("interactableObjectsMask").GetValue();
				initialized = true;
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "Start")]
		[HarmonyPrefix]
		private static void InitializePlayerControllerLate(PlayerControllerB __instance)
		{
			ReservedPlayerData value = new ReservedPlayerData(__instance);
			if (!allPlayerData.ContainsKey(__instance))
			{
				Plugin.Log("Initializing ReservedPlayerData for player: " + ((Object)__instance).name);
				allPlayerData.Add(__instance, value);
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "LateUpdate")]
		[HarmonyPostfix]
		private static void CheckForChangedInventorySize(PlayerControllerB __instance)
		{
			if ((!SyncManager.isSynced && !SyncManager.canUseModDisabledOnHost) || !ReservedPlayerData.allPlayerData.TryGetValue(__instance, out var value) || reservedHotbarSize <= 0 || value.hotbarSize == __instance.ItemSlots.Length)
			{
				return;
			}
			value.hotbarSize = __instance.ItemSlots.Length;
			int num = -1;
			if ((Object)(object)__instance == (Object)(object)localPlayerController)
			{
				if (HUDPatcher.reservedItemSlots != null && HUDPatcher.reservedItemSlots.Count > 0)
				{
					num = Array.IndexOf(HUDManager.Instance.itemSlotIconFrames, HUDPatcher.reservedItemSlots[0]);
					Plugin.Log("OnUpdateInventorySize A for local player: " + ((Object)__instance).name + " NewReservedItemsStartIndex: " + num);
				}
				if (num == -1)
				{
					for (int i = 0; i < HUDManager.Instance.itemSlotIconFrames.Length; i++)
					{
						if (((Object)HUDManager.Instance.itemSlotIconFrames[i]).name.ToLower().Contains("reserved"))
						{
							num = i;
							Plugin.Log("OnUpdateInventorySize B for local player: " + ((Object)__instance).name + " NewReservedItemsStartIndex: " + num);
							break;
						}
					}
				}
				PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
				foreach (PlayerControllerB val in allPlayerScripts)
				{
					if (ReservedPlayerData.allPlayerData.TryGetValue(val, out var value2) && value2 != value && reservedHotbarSize > 0 && value2.hotbarSize != val.ItemSlots.Length)
					{
						value2.reservedHotbarStartIndex = num;
					}
				}
			}
			if (num == -1)
			{
				num = value.reservedHotbarStartIndex;
				Plugin.Log("OnUpdateInventorySize C for player: " + ((Object)__instance).name + " NewReservedItemsStartIndex: " + num);
			}
			if (num == -1)
			{
				num = vanillaHotbarSize;
				Plugin.Log("OnUpdateInventorySize D for player: " + ((Object)__instance).name + " NewReservedItemsStartIndex: " + num);
			}
			value.reservedHotbarStartIndex = num;
			if (value.reservedHotbarStartIndex < 0)
			{
				Plugin.LogError("Set new reserved start index to slot: " + value.reservedHotbarStartIndex + ". Maybe share these logs with Flip? :)");
			}
			if (value.reservedHotbarEndIndexExcluded - 1 >= value.playerController.ItemSlots.Length)
			{
				Plugin.LogError("Set new reserved start index to slot: " + value.reservedHotbarStartIndex + " Last reserved slot index: " + (value.reservedHotbarEndIndexExcluded - 1) + " Inventory size: " + value.playerController.ItemSlots.Length + ". Maybe share these logs with Flip? :)");
			}
			if (value.isLocalPlayer)
			{
				HUDPatcher.UpdateUI();
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "BeginGrabObject")]
		[HarmonyPrefix]
		private static bool BeginGrabReservedItemPrefix(PlayerControllerB __instance)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if ((!SyncManager.isSynced && !SyncManager.canUseModDisabledOnHost) || !HUDPatcher.hasReservedItemSlotsAndEnabled)
			{
				return true;
			}
			localPlayerData.grabbingReservedItemSlotData = null;
			localPlayerData.grabbingReservedItemData = null;
			localPlayerData.grabbingReservedItem = null;
			localPlayerData.previousHotbarIndex = -1;
			if (__instance.twoHanded || __instance.sinkingValue > 0.73f)
			{
				return true;
			}
			Ray val = default(Ray);
			((Ray)(ref val))..ctor(((Component)__instance.gameplayCamera).transform.position, ((Component)__instance.gameplayCamera).transform.forward);
			RaycastHit val2 = default(RaycastHit);
			if (Physics.Raycast(val, ref val2, __instance.grabDistance, INTERACTABLE_OBJECT_MASK) && ((Component)((RaycastHit)(ref val2)).collider).gameObject.layer != 8 && ((Component)((RaycastHit)(ref val2)).collider).tag == "PhysicsProp")
			{
				GrabbableObject component = ((Component)((Component)((RaycastHit)(ref val2)).collider).transform).gameObject.GetComponent<GrabbableObject>();
				if ((Object)(object)component != (Object)null && !__instance.inSpecialInteractAnimation && !component.isHeld && !component.isPocketed)
				{
					NetworkObject networkObject = ((NetworkBehaviour)component).NetworkObject;
					if ((Object)(object)networkObject != (Object)null && networkObject.IsSpawned && SessionManager.TryGetUnlockedItemData(component, out var itemData))
					{
						localPlayerData.grabbingReservedItemData = itemData;
						localPlayerData.grabbingReservedItem = component;
						localPlayerData.previousHotbarIndex = Mathf.Clamp(__instance.currentItemSlot, 0, __instance.ItemSlots.Length - 1);
						Plugin.Log("Beginning grab on reserved item: " + itemData.itemName + " Previous item slot: " + localPlayerData.previousHotbarIndex);
					}
				}
			}
			return true;
		}

		[HarmonyPatch(typeof(PlayerControllerB), "BeginGrabObject")]
		[HarmonyPostfix]
		private static void BeginGrabReservedItemPostfix(PlayerControllerB __instance)
		{
			if (localPlayerData != null && localPlayerData.isGrabbingReservedItem && !localPlayerData.IsReservedItemSlot(localPlayerData.previousHotbarIndex))
			{
				SetSpecialGrabAnimationBool(__instance, setTrue: false);
				SetSpecialGrabAnimationBool(__instance, (Object)(object)localPlayerData.previouslyHeldItem != (Object)null, localPlayerData.previouslyHeldItem);
				__instance.playerBodyAnimator.SetBool("GrabValidated", true);
				__instance.playerBodyAnimator.SetBool("GrabInvalidated", false);
				__instance.playerBodyAnimator.ResetTrigger("SwitchHoldAnimation");
				__instance.playerBodyAnimator.ResetTrigger("SwitchHoldAnimationTwoHanded");
				if ((Object)(object)localPlayerData.previouslyHeldItem != (Object)null)
				{
					__instance.playerBodyAnimator.ResetTrigger(localPlayerData.previouslyHeldItem.itemProperties.pocketAnim);
				}
				__instance.twoHanded = (Object)(object)localPlayerData.previouslyHeldItem != (Object)null && localPlayerData.previouslyHeldItem.itemProperties.twoHanded;
				__instance.twoHandedAnimation = (Object)(object)localPlayerData.previouslyHeldItem != (Object)null && localPlayerData.previouslyHeldItem.itemProperties.twoHandedAnimation;
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "GrabObjectClientRpc")]
		[HarmonyPrefix]
		private static void GrabReservedItemClientRpcPrefix(bool grabValidated, NetworkObjectReference grabbedObject, PlayerControllerB __instance)
		{
			if ((!SyncManager.isSynced && !SyncManager.canUseModDisabledOnHost) || !NetworkHelper.IsClientExecStage((NetworkBehaviour)(object)__instance) || !ReservedPlayerData.allPlayerData.TryGetValue(__instance, out var value))
			{
				return;
			}
			NetworkObject val = default(NetworkObject);
			GrabbableObject val2 = default(GrabbableObject);
			if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsListening && grabValidated && ((NetworkObjectReference)(ref grabbedObject)).TryGet(ref val, (NetworkManager)null) && ((Component)val).TryGetComponent<GrabbableObject>(ref val2) && SessionManager.TryGetUnlockedItemData(val2, out var itemData))
			{
				ReservedItemSlotData firstEmptySlotForReservedItem = value.GetFirstEmptySlotForReservedItem(itemData.itemName);
				if (firstEmptySlotForReservedItem != null)
				{
					value.grabbingReservedItemSlotData = firstEmptySlotForReservedItem;
					value.grabbingReservedItemData = itemData;
					value.grabbingReservedItem = val2;
					value.previousHotbarIndex = Mathf.Clamp(__instance.currentItemSlot, 0, __instance.ItemSlots.Length - 1);
					return;
				}
			}
			value.grabbingReservedItemSlotData = null;
			value.grabbingReservedItemData = null;
			value.grabbingReservedItem = null;
			value.previousHotbarIndex = -1;
		}

		[HarmonyPatch(typeof(PlayerControllerB), "GrabObjectClientRpc")]
		[HarmonyPostfix]
		private static void GrabReservedItemClientRpcPostfix(bool grabValidated, NetworkObjectReference grabbedObject, PlayerControllerB __instance)
		{
			if ((!SyncManager.isSynced && !SyncManager.canUseModDisabledOnHost) || !NetworkHelper.IsClientExecStage((NetworkBehaviour)(object)__instance) || !ReservedPlayerData.allPlayerData.TryGetValue(__instance, out var value) || !value.isGrabbingReservedItem)
			{
				return;
			}
			if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsListening)
			{
				NetworkObject val = default(NetworkObject);
				GrabbableObject val2 = default(GrabbableObject);
				if (grabValidated && ((NetworkObjectReference)(ref grabbedObject)).TryGet(ref val, (NetworkManager)null) && ((Component)val).TryGetComponent<GrabbableObject>(ref val2))
				{
					if (SessionManager.TryGetUnlockedItemData(val2, out var itemData))
					{
						if (!value.IsReservedItemSlot(value.previousHotbarIndex))
						{
							if ((Object)(object)value.previouslyHeldItem != (Object)null)
							{
								value.previouslyHeldItem.EnableItemMeshes(true);
							}
							ReservedItemsPatcher.ForceEnableItemMesh(value.grabbingReservedItem, enabled: false);
							Traverse.Create((object)val2).Field("previousPlayerHeldBy").SetValue((object)__instance);
							if (value.isLocalPlayer)
							{
								int num = value.reservedHotbarStartIndex + value.grabbingReservedItemSlotData.GetReservedItemSlotIndex();
								((Component)HUDManager.Instance.itemSlotIconFrames[num]).GetComponent<Animator>().SetBool("selectedSlot", false);
								((Component)HUDManager.Instance.itemSlotIconFrames[value.previousHotbarIndex]).GetComponent<Animator>().SetBool("selectedSlot", true);
								((Component)HUDManager.Instance.itemSlotIconFrames[num]).GetComponent<Animator>().Play("PanelLines", 0, 1f);
								((Component)HUDManager.Instance.itemSlotIconFrames[value.previousHotbarIndex]).GetComponent<Animator>().Play("PanelEnlarge", 0, 1f);
							}
							else
							{
								SwitchToItemSlot(__instance, value.previousHotbarIndex);
								if (itemData.showOnPlayerWhileHolstered)
								{
									val2.EnableItemMeshes(true);
								}
							}
							SetSpecialGrabAnimationBool(__instance, setTrue: false);
							SetSpecialGrabAnimationBool(__instance, (Object)(object)value.previouslyHeldItem != (Object)null, value.previouslyHeldItem);
							__instance.playerBodyAnimator.SetBool("GrabValidated", true);
							__instance.playerBodyAnimator.SetBool("GrabInvalidated", false);
							__instance.playerBodyAnimator.ResetTrigger("SwitchHoldAnimation");
							__instance.playerBodyAnimator.ResetTrigger("SwitchHoldAnimationTwoHanded");
							if ((Object)(object)value.previouslyHeldItem != (Object)null)
							{
								__instance.playerBodyAnimator.ResetTrigger(value.previouslyHeldItem.itemProperties.pocketAnim);
							}
							__instance.twoHanded = (Object)(object)value.previouslyHeldItem != (Object)null && value.previouslyHeldItem.itemProperties.twoHanded;
							__instance.twoHandedAnimation = (Object)(object)value.previouslyHeldItem != (Object)null && value.previouslyHeldItem.itemProperties.twoHandedAnimation;
						}
						if (value.isLocalPlayer)
						{
							HUDPatcher.UpdateUI();
							return;
						}
						value.grabbingReservedItemSlotData = null;
						value.grabbingReservedItemData = null;
						value.grabbingReservedItem = null;
						value.previousHotbarIndex = -1;
						return;
					}
				}
				else if (value.isLocalPlayer)
				{
					Plugin.LogWarning("Failed to validate ReservedItemGrab by the local player. Object id: " + ((NetworkObjectReference)(ref grabbedObject)).NetworkObjectId + ". Internal error?");
					Traverse.Create((object)localPlayerController).Field("grabInvalidated").SetValue((object)true);
				}
				else
				{
					Plugin.LogWarning("Failed to validate ReservedItemGrab by player with id: " + ((Object)__instance).name + ". Object id: " + ((NetworkObjectReference)(ref grabbedObject)).NetworkObjectId + ". Internal error?");
				}
			}
			value.grabbingReservedItemSlotData = null;
			value.grabbingReservedItemData = null;
			value.grabbingReservedItem = null;
			value.previousHotbarIndex = -1;
		}

		[HarmonyPatch(typeof(GrabbableObject), "GrabItemOnClient")]
		[HarmonyPrefix]
		private static void OnReservedItemGrabbed(GrabbableObject __instance)
		{
			if (localPlayerData.grabbingReservedItemData != null && (Object)(object)__instance == (Object)(object)GetCurrentlyGrabbingObject(localPlayerController))
			{
				((MonoBehaviour)localPlayerController).StartCoroutine(OnReservedItemGrabbedEndOfFrame());
			}
			IEnumerator OnReservedItemGrabbedEndOfFrame()
			{
				yield return (object)new WaitForEndOfFrame();
				if (localPlayerData.isGrabbingReservedItem)
				{
					if (localPlayerData.previousHotbarIndex < 0 || localPlayerData.previousHotbarIndex >= localPlayerController.ItemSlots.Length || localPlayerData.IsReservedItemSlot(localPlayerData.previousHotbarIndex))
					{
						localPlayerData.previousHotbarIndex = 0;
					}
					SwitchToItemSlot(localPlayerController, localPlayerData.previousHotbarIndex);
					GrabbableObject obj = __instance;
					if (obj != null)
					{
						obj.PocketItem();
					}
					SetSpecialGrabAnimationBool(localPlayerController, setTrue: false);
					SetSpecialGrabAnimationBool(localPlayerController, (Object)(object)localPlayerData.previouslyHeldItem != (Object)null, localPlayerData.previouslyHeldItem);
					localPlayerController.playerBodyAnimator.SetBool("GrabValidated", true);
					localPlayerController.playerBodyAnimator.SetBool("GrabInvalidated", false);
					localPlayerController.playerBodyAnimator.ResetTrigger("SwitchHoldAnimation");
					localPlayerController.isGrabbingObjectAnimation = false;
					localPlayerController.playerBodyAnimator.ResetTrigger("SwitchHoldAnimationTwoHanded");
					if ((Object)(object)localPlayerData.previouslyHeldItem != (Object)null)
					{
						localPlayerController.playerBodyAnimator.ResetTrigger(localPlayerData.previouslyHeldItem.itemProperties.pocketAnim);
					}
					localPlayerController.twoHanded = (Object)(object)localPlayerData.previouslyHeldItem != (Object)null && localPlayerData.previouslyHeldItem.itemProperties.twoHanded;
					localPlayerController.twoHandedAnimation = (Object)(object)localPlayerData.previouslyHeldItem != (Object)null && localPlayerData.previouslyHeldItem.itemProperties.twoHandedAnimation;
				}
				localPlayerData.grabbingReservedItemSlotData = null;
				localPlayerData.grabbingReservedItemData = null;
				localPlayerData.grabbingReservedItem = null;
				localPlayerData.previousHotbarIndex = -1;
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "SwitchToItemSlot")]
		[HarmonyPrefix]
		private static void UpdateLastSelectedHotbarIndex(int slot, PlayerControllerB __instance)
		{
			int currentItemSlot = __instance.currentItemSlot;
			if (ReservedPlayerData.allPlayerData.TryGetValue(__instance, out var value))
			{
				if (value.IsReservedItemSlot(currentItemSlot))
				{
					ReservedHotbarManager.indexInReservedHotbar = currentItemSlot;
				}
				else
				{
					ReservedHotbarManager.indexInHotbar = currentItemSlot;
				}
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "SwitchToItemSlot")]
		[HarmonyPostfix]
		private static void UpdateFocusReservedHotbar(int slot, PlayerControllerB __instance)
		{
			if (HUDPatcher.hasReservedItemSlotsAndEnabled && ReservedPlayerData.allPlayerData.TryGetValue(__instance, out var value))
			{
				bool inReservedHotbarSlots = value.inReservedHotbarSlots;
				value.inReservedHotbarSlots = value.IsReservedItemSlot(__instance.currentItemSlot);
				bool flag = false;
				if (inReservedHotbarSlots != value.inReservedHotbarSlots || (value.inReservedHotbarSlots && ReservedHotbarManager.isToggledInReservedSlots && ReservedHotbarManager.currentlyToggledItemSlots != null && !ReservedHotbarManager.currentlyToggledItemSlots.Contains(value.GetCurrentlySelectedReservedItemSlot())))
				{
					flag = true;
				}
				if (value.inReservedHotbarSlots)
				{
					ReservedHotbarManager.OnSwapToReservedHotbar();
				}
				else
				{
					ReservedHotbarManager.OnSwapToVanillaHotbar();
				}
				if (flag)
				{
					HUDPatcher.UpdateToggledReservedItemSlotsUI();
				}
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "FirstEmptyItemSlot")]
		[HarmonyPostfix]
		private static void GetReservedItemSlotPlacementIndex(ref int __result, PlayerControllerB __instance)
		{
			if (reservedHotbarSize <= 0 || !HUDPatcher.hasReservedItemSlotsAndEnabled || !ReservedPlayerData.allPlayerData.TryGetValue(__instance, out var value))
			{
				return;
			}
			ReservedItemData grabbingReservedItemData = value.grabbingReservedItemData;
			if (grabbingReservedItemData != null)
			{
				ReservedItemSlotData firstEmptySlotForReservedItem = value.GetFirstEmptySlotForReservedItem(grabbingReservedItemData.itemName);
				if (firstEmptySlotForReservedItem != null)
				{
					__result = firstEmptySlotForReservedItem.GetIndexInInventory(__instance);
					return;
				}
				value.grabbingReservedItemSlotData = null;
				value.grabbingReservedItemData = null;
				value.grabbingReservedItem = null;
				value.previousHotbarIndex = -1;
			}
			if (!value.IsReservedItemSlot(__result))
			{
				return;
			}
			__result = -1;
			for (int i = 0; i < __instance.ItemSlots.Length; i++)
			{
				if (!value.IsReservedItemSlot(i) && (Object)(object)__instance.ItemSlots[i] == (Object)null)
				{
					__result = i;
					break;
				}
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "NextItemSlot")]
		[HarmonyPostfix]
		private static void OnNextItemSlot(ref int __result, bool forward, PlayerControllerB __instance)
		{
			if (reservedHotbarSize <= 0 || !HUDPatcher.hasReservedItemSlotsAndEnabled || !ReservedPlayerData.allPlayerData.TryGetValue(__instance, out var value))
			{
				return;
			}
			bool inReservedHotbarSlots = value.inReservedHotbarSlots;
			bool flag = value.IsReservedItemSlot(__result);
			bool flag2 = inReservedHotbarSlots;
			if (inReservedHotbarSlots)
			{
				ReservedItemSlotData unlockedReservedItemSlot = SessionManager.GetUnlockedReservedItemSlot(__result - value.reservedHotbarStartIndex);
				if (ReservedHotbarManager.isToggledInReservedSlots && !Keybinds.pressedToggleKey && !Keybinds.holdingModifierKey && ReservedHotbarManager.currentlyToggledItemSlots != null && (!flag || (Object)(object)value.itemSlots[__result] == (Object)null || !ReservedHotbarManager.currentlyToggledItemSlots.Contains(unlockedReservedItemSlot)))
				{
					__result = ReservedHotbarManager.indexInHotbar;
					return;
				}
			}
			if (flag == flag2 && (!flag || (Object)(object)__instance.ItemSlots[__result] != (Object)null))
			{
				return;
			}
			int num = (forward ? 1 : (-1));
			__result = __instance.currentItemSlot + num;
			__result = ((__result < 0) ? (__instance.ItemSlots.Length - 1) : ((__result < __instance.ItemSlots.Length) ? __result : 0));
			flag = value.IsReservedItemSlot(__result);
			if (!flag2)
			{
				if (flag)
				{
					__result = (forward ? ((value.reservedHotbarStartIndex + reservedHotbarSize) % __instance.ItemSlots.Length) : (value.reservedHotbarStartIndex - 1));
				}
				return;
			}
			__result = (flag ? __result : (forward ? value.reservedHotbarStartIndex : (value.reservedHotbarStartIndex + reservedHotbarSize - 1)));
			int numHeldReservedItems = value.GetNumHeldReservedItems();
			while (numHeldReservedItems > 0 && __result != value.currentItemSlot && (Object)(object)__instance.ItemSlots[__result] == (Object)null)
			{
				__result += num;
				__result = ((!value.IsReservedItemSlot(__result)) ? (forward ? value.reservedHotbarStartIndex : (value.reservedHotbarStartIndex + reservedHotbarSize - 1)) : __result);
			}
		}

		[HarmonyPatch(typeof(HUDManager), "ClearControlTips")]
		[HarmonyPrefix]
		private static bool PreventClearControlTipsGrabbingReservedItem(HUDManager __instance)
		{
			return ReservedPlayerData.localPlayerData == null || (Object)(object)ReservedPlayerData.localPlayerData.grabbingReservedItem == (Object)null;
		}

		[HarmonyPatch(typeof(GrabbableObject), "SetControlTipsForItem")]
		[HarmonyPrefix]
		private static bool PreventUpdateControlTipsGrabbingReservedItem(GrabbableObject __instance)
		{
			return ReservedPlayerData.localPlayerData == null || (Object)(object)ReservedPlayerData.localPlayerData.grabbingReservedItem != (Object)(object)__instance;
		}

		private static GrabbableObject GetCurrentlyGrabbingObject(PlayerControllerB playerController)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			return (GrabbableObject)Traverse.Create((object)playerController).Field("currentlyGrabbingObject").GetValue();
		}

		private static void SetCurrentlyGrabbingObject(PlayerControllerB playerController, GrabbableObject grabbable)
		{
			Traverse.Create((object)playerController).Field("currentlyGrabbingObject").SetValue((object)grabbable);
		}

		public static bool ReservedItemIsBeingGrabbed(GrabbableObject grabbableObject)
		{
			if ((Object)(object)grabbableObject == (Object)null)
			{
				return false;
			}
			foreach (ReservedPlayerData value in ReservedPlayerData.allPlayerData.Values)
			{
				if ((Object)(object)grabbableObject == (Object)(object)value.grabbingReservedItem)
				{
					return true;
				}
			}
			return false;
		}

		public static void SetSpecialGrabAnimationBool(PlayerControllerB playerController, bool setTrue, GrabbableObject currentItem = null)
		{
			MethodInfo method = ((object)playerController).GetType().GetMethod("SetSpecialGrabAnimationBool", BindingFlags.Instance | BindingFlags.NonPublic);
			method.Invoke(playerController, new object[2] { setTrue, currentItem });
		}

		public static void SwitchToItemSlot(PlayerControllerB playerController, int slot, GrabbableObject fillSlotWithItem = null)
		{
			MethodInfo method = ((object)playerController).GetType().GetMethod("SwitchToItemSlot", BindingFlags.Instance | BindingFlags.NonPublic);
			method.Invoke(playerController, new object[2] { slot, fillSlotWithItem });
			if (ReservedPlayerData.allPlayerData.TryGetValue(playerController, out var value))
			{
				value.timeSinceSwitchingSlots = 0f;
			}
		}
	}
	[HarmonyPatch]
	internal static class ReservedItemsPatcher
	{
		public static bool ignoreMeshOverride;

		public static PlayerControllerB localPlayerController => StartOfRound.Instance?.localPlayerController;

		[HarmonyPatch(typeof(GrabbableObject), "PocketItem")]
		[HarmonyPostfix]
		private static void OnPocketReservedItem(GrabbableObject __instance)
		{
			if (!ConfigSettings.showReservedItemsHolstered.Value || (Object)(object)__instance.playerHeldBy == (Object)null || !ReservedPlayerData.allPlayerData.TryGetValue(__instance.playerHeldBy, out var value) || !SessionManager.TryGetUnlockedItemData(__instance, out var itemData) || !value.IsItemInReservedItemSlot(__instance) || !itemData.showOnPlayerWhileHolstered)
			{
				return;
			}
			MeshRenderer[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<MeshRenderer>();
			foreach (MeshRenderer val in componentsInChildren)
			{
				if (!((Component)val).gameObject.CompareTag("DoNotSet") && !((Component)val).gameObject.CompareTag("InteractTrigger") && ((Component)val).gameObject.layer != 14 && ((Component)val).gameObject.layer != 22)
				{
					((Component)val).gameObject.layer = (value.isLocalPlayer ? 23 : 6);
				}
			}
			__instance.parentObject = value.boneMap.GetBone(itemData.holsteredParentBone);
			ForceEnableItemMesh(__instance, enabled: true);
		}

		[HarmonyPatch(typeof(GrabbableObject), "EquipItem")]
		[HarmonyPostfix]
		private static void OnEquipReservedItem(GrabbableObject __instance)
		{
			if (!ConfigSettings.showReservedItemsHolstered.Value || (Object)(object)__instance.playerHeldBy == (Object)null || !ReservedPlayerData.allPlayerData.TryGetValue(__instance.playerHeldBy, out var value) || !SessionManager.TryGetUnlockedItemData(__instance, out var itemData) || !value.IsItemInReservedItemSlot(__instance) || !itemData.showOnPlayerWhileHolstered)
			{
				return;
			}
			MeshRenderer[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<MeshRenderer>();
			foreach (MeshRenderer val in componentsInChildren)
			{
				if (!((Component)val).gameObject.CompareTag("DoNotSet") && !((Component)val).gameObject.CompareTag("InteractTrigger") && ((Component)val).gameObject.layer != 14 && ((Component)val).gameObject.layer != 22)
				{
					((Component)val).gameObject.layer = 6;
				}
			}
			__instance.parentObject = (value.isLocalPlayer ? __instance.playerHeldBy.localItemHolder : __instance.playerHeldBy.serverItemHolder);
		}

		[HarmonyPatch(typeof(GrabbableObject), "DiscardItem")]
		[HarmonyPostfix]
		private static void ResetReservedItemLayer(GrabbableObject __instance)
		{
			if (!SessionManager.TryGetUnlockedItemData(__instance, out var itemData) || !itemData.showOnPlayerWhileHolstered)
			{
				return;
			}
			MeshRenderer[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<MeshRenderer>();
			foreach (MeshRenderer val in componentsInChildren)
			{
				if (!((Component)val).gameObject.CompareTag("DoNotSet") && !((Component)val).gameObject.CompareTag("InteractTrigger") && ((Component)val).gameObject.layer != 14 && ((Component)val).gameObject.layer != 22)
				{
					((Component)val).gameObject.layer = 6;
				}
			}
		}

		[HarmonyPatch(typeof(GrabbableObject), "LateUpdate")]
		[HarmonyPostfix]
		private static void SetHolsteredPositionRotation(GrabbableObject __instance)
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: 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)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			if (ConfigSettings.showReservedItemsHolstered.Value && !((Object)(object)__instance.playerHeldBy == (Object)null) && !((Object)(object)__instance.parentObject == (Object)null) && ReservedPlayerData.allPlayerData.TryGetValue(__instance.playerHeldBy, out var value) && SessionManager.TryGetUnlockedItemData(__instance, out var itemData) && value.IsItemInReservedItemSlot(__instance) && itemData.showOnPlayerWhileHolstered && (Object)(object)__instance != (Object)(object)value.currentlySelectedItem)
			{
				Transform transform = ((Component)__instance.parentObject).transform;
				((Component)__instance).transform.rotation = ((Component)__instance.parentObject).transform.rotation * Quaternion.Euler(itemData.holsteredRotationOffset);
				((Component)__instance).transform.position = transform.position + transform.rotation * itemData.holsteredPositionOffset;
			}
		}

		[HarmonyPatch(typeof(GrabbableObject), "EnableItemMeshes")]
		[HarmonyPrefix]
		private static void OnEnableItemMeshes(ref bool enable, GrabbableObject __instance)
		{
			if (ConfigSettings.showReservedItemsHolstered.Value)
			{
				if ((Object)(object)__instance.playerHeldBy != (Object)null && !ignoreMeshOverride && ReservedPlayerData.allPlayerData.TryGetValue(__instance.playerHeldBy, out var value) && SessionManager.TryGetUnlockedItemData(__instance, out var itemData) && value.IsItemInReservedItemSlot(__instance) && itemData.showOnPlayerWhileHolstered && (Object)(object)value.currentlySelectedItem != (Object)(object)__instance && !PlayerPatcher.ReservedItemIsBeingGrabbed(__instance))
				{
					enable = true;
				}
				ignoreMeshOverride = false;
			}
		}

		public static void ForceEnableItemMesh(GrabbableObject grabbableObject, bool enabled)
		{
			ignoreMeshOverride = true;
			grabbableObject.EnableItemMeshes(enabled);
		}
	}
	[HarmonyPatch]
	internal static class SyncAlreadyHeldObjectsPatcher
	{
		[HarmonyPatch(typeof(StartOfRound), "SyncAlreadyHeldObjectsClientRpc")]
		[HarmonyPrefix]
		private static bool SyncAlreadyHeldReservedObjectsClientRpc(ref NetworkObjectReference[] gObjects, ref int[] playersHeldBy, ref int[] itemSlotNumbers, ref int[] isObjectPocketed, int syncWithClient, StartOfRound __instance)
		{
			if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsListening)
			{
				return true;
			}
			if ((NetworkHelper.IsClientExecStage((NetworkBehaviour)(object)__instance) || (!NetworkManager.Singleton.IsServer && !NetworkManager.Singleton.IsHost)) && (!NetworkHelper.IsClientExecStage((NetworkBehaviour)(object)__instance) || (!NetworkManager.Singleton.IsClient && !NetworkManager.Singleton.IsHost) || syncWithClient != (int)NetworkManager.Singleton.LocalClientId))
			{
				return false;
			}
			bool flag = false;
			List<NetworkObjectReference> list = new List<NetworkObjectReference>(gObjects);
			List<int> list2 = new List<int>(playersHeldBy);
			List<int> list3 = new List<int>(itemSlotNumbers);
			List<int> list4 = new List<int>(isObjectPocketed);
			for (int num = itemSlotNumbers.Length - 1; num >= 0; num--)
			{
				if (itemSlotNumbers[num] >= __instance.localPlayerController.ItemSlots.Length)
				{
					list.RemoveAt(num);
					list2.RemoveAt(num);
					list3.RemoveAt(num);
					list4.Remove(num);
					flag = true;
				}
			}
			if (flag)
			{
				gObjects = list.ToArray();
				playersHeldBy = list2.ToArray();
				itemSlotNumbers = list3.ToArray();
				isObjectPocketed = list4.ToArray();
			}
			return true;
		}
	}
	[HarmonyPatch]
	internal static class TerminalPatcher
	{
		public static Terminal terminalInstance;

		public static bool initializedTerminalNodes;

		public static ReservedItemSlotData purchasingItemSlot;

		[HarmonyPatch(typeof(Terminal), "Awake")]
		[HarmonyPrefix]
		public static void InitializeTerminal(Terminal __instance)
		{
			terminalInstance = __instance;
			initializedTerminalNodes = false;
			EditExistingTerminalNodes();
		}

		[HarmonyPatch(typeof(Terminal), "BeginUsingTerminal")]
		[HarmonyPrefix]
		public static void OnBeginUsingTerminal(Terminal __instance)
		{
			if (!initializedTerminalNodes && SyncManager.isSynced)
			{
				EditExistingTerminalNodes();
			}
		}

		public static void EditExistingTerminalNodes()
		{
			if (!SyncManager.isSynced)
			{
				return;
			}
			initializedTerminalNodes = true;
			if (!SyncManager.enablePurchasingItemSlots)
			{
				return;
			}
			foreach (TerminalNode specialNode in terminalInstance.terminalNodes.specialNodes)
			{
				if (((Object)specialNode).name == "Start" && !specialNode.displayText.Contains("[ReservedItemSlots]"))
				{
					string text = "Type \"Help\" for a list of commands.";
					int num = specialNode.displayText.IndexOf(text);
					if (num != -1)
					{
						num += text.Length;
						string value = "\n\n[ReservedItemSlots]\nType \"Reserved\" to purchase reserved item slots.";
						specialNode.displayText = specialNode.displayText.Insert(num, value);
					}
					else
					{
						Plugin.LogError("Failed to add reserved item slots tip to terminal. Maybe an update broke it?");
					}
				}
				else if (((Object)specialNode).name == "HelpCommands" && !specialNode.displayText.Contains(">RESERVED"))
				{
					string value2 = "[numberOfItemsOnRoute]";
					int num2 = specialNode.displayText.IndexOf(value2);
					if (num2 != -1)
					{
						string text2 = ">RESERVED\n";
						text2 += "Purchase reserved item slots.\n\n";
						specialNode.displayText = specialNode.displayText.Insert(num2, text2);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Terminal), "TextPostProcess")]
		[HarmonyPrefix]
		public static void TextPostProcess(ref string modifiedDisplayText, TerminalNode node)
		{
			if (modifiedDisplayText.Length <= 0)
			{
				return;
			}
			string text = "[[[reservedItemSlotsSelectionList]]]";
			if (!modifiedDisplayText.Contains(text))
			{
				return;
			}
			int num = modifiedDisplayText.IndexOf(text);
			int num2 = num + text.Length;
			string oldValue = modifiedDisplayText.Substring(num, num2 - num);
			string text2 = "";
			if (!SyncManager.enablePurchasingItemSlots)
			{
				text2 += "Every reserved item slot is unlocked!\n\n";
			}
			else
			{
				text2 += "Reserved Item Slots\n------------------------------\n\n";
				text2 += "To purchase a reserved item slot, type the following command.\n> RESERVED [item_slot]\n\n";
				int num3 = 0;
				foreach (ReservedItemSlotData value in SyncManager.unlockableReservedItemSlotsDict.Values)
				{
					num3 = Mathf.Max(num3, value.slotName.Length);
				}
				foreach (ReservedItemSlotData value2 in SyncManager.unlockableReservedItemSlotsDict.Values)
				{
					string arg = (SessionManager.IsItemSlotUnlocked(value2) ? "[Purchased]" : ("$" + value2.purchasePrice));
					text2 += $"* {value2.slotDisplayName}{new string(' ', num3 - value2.slotDisplayName.Length)}   //   {arg}\n";
				}
			}
			modifiedDisplayText = modifiedDisplayText.Replace(oldValue, text2);
		}

		[HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")]
		[HarmonyPrefix]
		public static bool ParsePlayerSentence(ref TerminalNode __result, Terminal __instance)
		{
			if (__instance.screenText.text.Length <= 0)
			{
				return true;
			}
			string text = __instance.screenText.text.Substring(__instance.screenText.text.Length - __instance.textAdded).ToLower();
			string[] array = text.Split(new char[1] { ' ' });
			ReservedItemSlotData reservedItemSlotData = null;
			if (!SyncManager.isSynced)
			{
				if (text.StartsWith("reserved"))
				{
					__result = BuildTerminalNodeHostDoesNotHaveMod();
					return false;
				}
				return true;
			}
			if (purchasingItemSlot != null)
			{
				if ("confirm".StartsWith(text))
				{
					if (purchasingItemSlot.isUnlocked)
					{
						Plugin.LogWarning("Attempted to confirm purchase on reserved item slot that was already unlocked. Item slot: " + purchasingItemSlot.slotDisplayName);
						__result = BuildTerminalNodeAlreadyUnlocked(purchasingItemSlot);
					}
					else if (terminalInstance.groupCredits < purchasingItemSlot.purchasePrice)
					{
						Plugin.LogWarning("Attempted to confirm purchase with insufficient credits. Current credits: " + terminalInstance.groupCredits + " Required credits: " + purchasingItemSlot.purchasePrice);
						__result = BuildTerminalNodeInsufficientFunds(purchasingItemSlot);
					}
					else
					{
						Plugin.Log("Purchasing reserved item slot: " + purchasingItemSlot.slotDisplayName + ". Price: " + purchasingItemSlot.purchasePrice);
						Terminal obj = terminalInstance;
						obj.groupCredits -= purchasingItemSlot.purchasePrice;
						terminalInstance.BuyItemsServerRpc(new int[0], terminalInstance.groupCredits, terminalInstance.numberOfItemsInDropship);
						SyncManager.SendUnlockItemSlotUpdateToServer(purchasingItemSlot.slotId);
						__result = BuildTerminalNodeOnPurchased(purchasingItemSlot, terminalInstance.groupCredits);
					}
				}
				else
				{
					Plugin.Log("Canceling order.");
					__result = BuildCustomTerminalNode("Canceled order.\n\n");
				}
				purchasingItemSlot = null;
				return false;
			}
			purchasingItemSlot = null;
			if (array.Length == 0 || array[0] != "reserved")
			{
				return true;
			}
			if (array.Length == 1)
			{
				__result = BuildTerminalNodeHome();
				return false;
			}
			string text2 = text.Substring(9);
			reservedItemSlotData = TryGetReservedItemSlot(text2);
			if (reservedItemSlotData != null)
			{
				if (SessionManager.IsItemSlotUnlocked(reservedItemSlotData))
				{
					Plugin.LogWarning("Attempted to start purchase on reserved item slot that was already unlocked. Item slot: " + reservedItemSlotData.slotName);
					__result = BuildTerminalNodeAlreadyUnlocked(reservedItemSlotData);
				}
				else if (terminalInstance.groupCredits < reservedItemSlotData.purchasePrice)
				{
					Plugin.LogWarning("Attempted to start purchase with insufficient credits. Current credits: " + terminalInstance.groupCredits + ". Item slot price: " + reservedItemSlotData.purchasePrice);
					__result = BuildTerminalNodeInsufficientFunds(reservedItemSlotData);
				}
				else
				{
					Plugin.Log("Started purchasing reserved item slot: " + reservedItemSlotData.slotName);
					purchasingItemSlot = reservedItemSlotData;
					__result = BuildTerminalNodeConfirmDenyPurchase(reservedItemSlotData);
				}
				return false;
			}
			Plugin.LogWarning("Attempted to start purchase on invalid reserved item slot. Item slot: " + text2);
			__result = BuildTerminalNodeInvalidReservedItemSlot(text2);
			return false;
		}

		private static TerminalNode BuildTerminalNodeHome()
		{
			//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_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			return new TerminalNode
			{
				displayText = "[ReservedItemSlots]\n\nStore\n------------------------------\n[[[reservedItemSlotsSelectionList]]]\n\n",
				clearPreviousText = true,
				acceptAnything = false
			};
		}

		private static TerminalNode BuildTerminalNodeConfirmDenyPurchase(ReservedItemSlotData itemSlotData)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			TerminalNode val = new TerminalNode();
			val.displayText = "You have requested to purchase a reserved item slot for $" + itemSlotData.purchasePrice + " credits.\n> [" + itemSlotData.slotDisplayName + "]\n\n";
			val.isConfirmationNode = true;
			val.acceptAnything = false;
			val.clearPreviousText = true;
			TerminalNode val2 = val;
			val2.displayText = val2.displayText + "Credit balance: $" + terminalInstance.groupCredits + "\n";
			val2.displayText += "\n";
			val2.displayText += "Please CONFIRM or DENY.\n\n";
			return val2;
		}

		private static TerminalNode BuildTerminalNodeOnPurchased(ReservedItemSlotData itemSlotData, int newGroupCredits)
		{
			//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_0021: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			TerminalNode val = new TerminalNode
			{
				displayText = "You have successfully purchased a new reserved item slot!\n> [" + itemSlotData.slotDisplayName + "]\n\n",
				buyUnlockable = true,
				clearPreviousText = true,
				acceptAnything = false,
				playSyncedClip = 0
			};
			val.displayText = val.displayText + "New credit balance: $" + newGroupCredits + "\n\n";
			return val;
		}

		private static TerminalNode BuildTerminalNodeAlreadyUnlocked(ReservedItemSlotData itemSlot)
		{
			//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_0021: 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_0030: Expected O, but got Unknown
			return new TerminalNode
			{
				displayText = "You have already purchased this reserved item slot!\n> [" + itemSlot.slotDisplayName + "]\n\n",
				clearPreviousText = false,
				acceptAnything = false
			};
		}

		private static TerminalNode BuildTerminalNodeInsufficientFunds(ReservedItemSlotData itemSlot)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			TerminalNode val = new TerminalNode();
			val.displayText = "You could not afford this reserved item slot!\n> [" + itemSlot.slotDisplayName + "]\n\nCredit balance is $" + terminalInstance.groupCredits + "\n";
			val.clearPreviousText = true;
			val.acceptAnything = false;
			TerminalNode val2 = val;
			val2.displayText = val2.displayText + "Price of reserved item slot is $" + itemSlot.purchasePrice + "\n\n";
			return val2;
		}

		private static TerminalNode BuildTerminalNodeInvalidReservedItemSlot(string reservedItemSlotName = "")
		{
			//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_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			TerminalNode val = new TerminalNode
			{
				displayText = "Reserved item slot does not exist.",
				clearPreviousText = false,
				acceptAnything = false
			};
			if (reservedItemSlotName != "")
			{
				val.displayText = val.displayText + "\n\"" + reservedItemSlotName + "\"";
			}
			va