Decompiled source of ExtraInventorySlots v1.0.0

plugins\ExtraInventorySlots\ExtraInventorySlots.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ExtraInventorySlots
{
	[BepInPlugin("DarkSpider90.ExtraInventorySlots", "Extra Inventory Slots", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal const string PluginGuid = "DarkSpider90.ExtraInventorySlots";

		internal const string PluginName = "Extra Inventory Slots";

		internal const string PluginVersion = "1.0.0";

		internal const int VanillaSlotCount = 3;

		private const int MaxSlotCount = 10;

		private Harmony _harmony;

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal static ConfigEntry<int> SlotCount { get; private set; }

		internal static ConfigEntry<bool> HostProtection { get; private set; }

		internal static ConfigEntry<bool> KeepItemsInTruck { get; private set; }

		internal static ConfigEntry<bool> ExtraHotkeys { get; private set; }

		internal static ConfigEntry<bool> NumpadHotkeys { get; private set; }

		internal static ConfigEntry<bool> AutoSwapItems { get; private set; }

		internal static int EffectiveSlotCount => Mathf.Clamp(SlotCount?.Value ?? 3, 3, 10);

		private void Awake()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			SlotCount = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Number Of Slots", 5, new ConfigDescription("Total inventory slots to show and use. Vanilla is 3. Changes after a scene or round reload.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 10), Array.Empty<object>()));
			HostProtection = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Host Protection", true, "If true, a host with this mod blocks clients from equipping items into slots above the host's configured slot count.");
			KeepItemsInTruck = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Keep Items In Truck", false, "If true, extra-slot items are not restored back to players when the game rebuilds item ownership between rounds.");
			AutoSwapItems = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Auto Swap Items", true, "If true, storing a held item into an occupied inventory slot swaps it with the item already in that slot.");
			ExtraHotkeys = ((BaseUnityPlugin)this).Config.Bind<bool>("Controls", "Extra Slot Hotkeys", true, "If true, number keys 4-9 and 0 control extra inventory slots.");
			NumpadHotkeys = ((BaseUnityPlugin)this).Config.Bind<bool>("Controls", "Numpad Hotkeys", true, "If true, numpad keys also control matching extra inventory slots.");
			_harmony = new Harmony("DarkSpider90.ExtraInventorySlots");
			_harmony.PatchAll();
			Log.LogInfo((object)"Extra Inventory Slots v1.0.0 loaded for R.E.P.O. v0.4.0.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class AutoItemSwap
	{
		private const float EquipWaitTimeout = 0.75f;

		private static readonly FieldInfo GrabbedPhysGrabObjectField = AccessTools.Field(typeof(PhysGrabber), "grabbedPhysGrabObject");

		private static readonly FieldInfo PlayerInputDisableTimerField = AccessTools.Field(typeof(PlayerController), "InputDisableTimer");

		private static readonly FieldInfo LastEquipTimeField = AccessTools.Field(typeof(InventorySpot), "lastEquipTime");

		private static readonly FieldInfo EquipCooldownField = AccessTools.Field(typeof(InventorySpot), "equipCooldown");

		private static readonly FieldInfo HandleInputField = AccessTools.Field(typeof(InventorySpot), "handleInput");

		internal static bool PrefixHandleInput(InventorySpot spot)
		{
			if (!Plugin.AutoSwapItems.Value || (Object)(object)spot == (Object)null)
			{
				return true;
			}
			if (ShouldLetVanillaHandle(spot))
			{
				return true;
			}
			ItemEquippable heldItem = GetHeldItem();
			if ((Object)(object)heldItem == (Object)null || !spot.IsOccupied())
			{
				return true;
			}
			ItemEquippable currentItem = spot.CurrentItem;
			if ((Object)(object)currentItem == (Object)null || (Object)(object)currentItem == (Object)(object)heldItem)
			{
				return true;
			}
			int inventorySpotIndex = spot.inventorySpotIndex;
			int ownerViewId = (SemiFunc.IsMultiplayer() ? PhysGrabber.instance.photonView.ViewID : (-1));
			SetInputCooldown(spot);
			currentItem.RequestUnequip();
			((MonoBehaviour)Plugin.Instance).StartCoroutine(EquipWhenSlotIsReady(heldItem, inventorySpotIndex, ownerViewId, currentItem));
			return false;
		}

		private static bool ShouldLetVanillaHandle(InventorySpot spot)
		{
			if (SemiFunc.RunIsArena())
			{
				return true;
			}
			if ((Object)(object)PlayerController.instance != (Object)null && PlayerInputDisableTimerField != null && (float)PlayerInputDisableTimerField.GetValue(PlayerController.instance) > 0f)
			{
				return true;
			}
			return IsInCooldown(spot);
		}

		private static bool IsInCooldown(InventorySpot spot)
		{
			if (LastEquipTimeField == null || EquipCooldownField == null || HandleInputField == null)
			{
				return false;
			}
			if ((bool)HandleInputField.GetValue(spot))
			{
				return false;
			}
			float num = (float)LastEquipTimeField.GetValue(spot);
			float num2 = (float)EquipCooldownField.GetValue(spot);
			return Time.time - num < num2;
		}

		private static void SetInputCooldown(InventorySpot spot)
		{
			LastEquipTimeField?.SetValue(spot, Time.time);
			HandleInputField?.SetValue(spot, false);
		}

		private static ItemEquippable GetHeldItem()
		{
			PhysGrabber instance = PhysGrabber.instance;
			if ((Object)(object)instance == (Object)null || !instance.grabbed)
			{
				return null;
			}
			object? obj = GrabbedPhysGrabObjectField?.GetValue(instance);
			PhysGrabObject val = (PhysGrabObject)((obj is PhysGrabObject) ? obj : null);
			if (!((Object)(object)val == (Object)null))
			{
				return ((Component)val).GetComponent<ItemEquippable>();
			}
			return null;
		}

		private static IEnumerator EquipWhenSlotIsReady(ItemEquippable heldItem, int slotIndex, int ownerViewId, ItemEquippable previousSlotItem)
		{
			float timeoutAt = Time.time + 0.75f;
			while (Time.time < timeoutAt)
			{
				InventorySpot spot = GetSpot(slotIndex);
				if ((Object)(object)spot == (Object)null || !spot.IsOccupied() || (Object)(object)spot.CurrentItem != (Object)(object)previousSlotItem)
				{
					break;
				}
				yield return null;
			}
			if ((Object)(object)heldItem != (Object)null && !heldItem.IsEquipped())
			{
				heldItem.RequestEquip(slotIndex, ownerViewId);
			}
		}

		private static InventorySpot GetSpot(int slotIndex)
		{
			if ((Object)(object)Inventory.instance == (Object)null)
			{
				return null;
			}
			InventorySlotList.EnsureSlots(Inventory.instance, slotIndex + 1);
			List<InventorySpot> allSpots = Inventory.instance.GetAllSpots();
			if (slotIndex < 0 || slotIndex >= allSpots.Count)
			{
				return null;
			}
			return allSpots[slotIndex];
		}
	}
	internal static class ExtraSlotState
	{
		private static readonly Dictionary<string, Dictionary<int, int>> ServerMonitoredInventoryItems = new Dictionary<string, Dictionary<int, int>>();

		internal static void TrackInventoryUpdate(string steamId, string itemName, int spot)
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer() || spot < 3 || string.IsNullOrEmpty(steamId))
			{
				return;
			}
			if (string.IsNullOrEmpty(itemName))
			{
				if (ServerMonitoredInventoryItems.TryGetValue(steamId, out var value))
				{
					value.Remove(spot);
					if (value.Count == 0)
					{
						ServerMonitoredInventoryItems.Remove(steamId);
					}
				}
			}
			else
			{
				if (!ServerMonitoredInventoryItems.TryGetValue(steamId, out var value2))
				{
					value2 = new Dictionary<int, int>();
					ServerMonitoredInventoryItems[steamId] = value2;
				}
				value2[spot] = itemName.GetHashCode();
			}
		}

		internal static bool TryFindItemOwnerAndSpot(int itemHash, out PlayerAvatar owner, out int spot)
		{
			owner = null;
			spot = -1;
			List<PlayerAvatar> list = SemiFunc.PlayerGetList();
			if (list == null)
			{
				return false;
			}
			foreach (PlayerAvatar item in list)
			{
				string key = SemiFunc.PlayerGetSteamID(item);
				if (!ServerMonitoredInventoryItems.TryGetValue(key, out var value))
				{
					continue;
				}
				foreach (KeyValuePair<int, int> item2 in value)
				{
					if (item2.Value == itemHash)
					{
						owner = item;
						spot = item2.Key;
						return true;
					}
				}
			}
			return false;
		}

		internal static void Clear()
		{
			ServerMonitoredInventoryItems.Clear();
		}
	}
	internal static class InventorySlotList
	{
		internal static void EnsureSlots(Inventory inventory, int minimumSlotCount = -1)
		{
			if (!((Object)(object)inventory == (Object)null))
			{
				List<InventorySpot> allSpots = inventory.GetAllSpots();
				int num = Math.Max(Plugin.EffectiveSlotCount, minimumSlotCount);
				while (allSpots.Count < num)
				{
					allSpots.Add(null);
				}
			}
		}
	}
	internal static class InventoryBatteryBinding
	{
		private static readonly FieldInfo BatteryVisualLogicField = AccessTools.Field(typeof(InventorySpot), "batteryVisualLogic");

		private static readonly FieldInfo BarsField = AccessTools.Field(typeof(BatteryVisualLogic), "bars");

		private static readonly FieldInfo TargetScaleField = AccessTools.Field(typeof(BatteryVisualLogic), "targetScale");

		private static readonly FieldInfo TargetScaleOriginalField = AccessTools.Field(typeof(BatteryVisualLogic), "targetScaleOriginal");

		private static readonly FieldInfo TargetRotationField = AccessTools.Field(typeof(BatteryVisualLogic), "targetRotation");

		private static readonly FieldInfo TargetRotationOriginalField = AccessTools.Field(typeof(BatteryVisualLogic), "targetRotationOriginal");

		private static readonly FieldInfo TargetPositionField = AccessTools.Field(typeof(BatteryVisualLogic), "targetPosition");

		private static readonly FieldInfo TargetPositionOriginalField = AccessTools.Field(typeof(BatteryVisualLogic), "targetPositionOriginal");

		private static readonly FieldInfo DoOutroField = AccessTools.Field(typeof(BatteryVisualLogic), "doOutro");

		private static readonly FieldInfo SpringScaleField = AccessTools.Field(typeof(BatteryVisualLogic), "springScale");

		private static readonly FieldInfo SpringRotationField = AccessTools.Field(typeof(BatteryVisualLogic), "springRotation");

		private static readonly FieldInfo SpringPositionField = AccessTools.Field(typeof(BatteryVisualLogic), "springPosition");

		private static readonly FieldInfo SpringFloatLastPositionField = AccessTools.Field(typeof(SpringFloat), "lastPosition");

		private static readonly FieldInfo SpringVectorLastPositionField = AccessTools.Field(typeof(SpringVector3), "lastPosition");

		internal static BatteryVisualLogic Bind(InventorySpot spot, bool activateForVanillaStart)
		{
			if ((Object)(object)spot == (Object)null)
			{
				return null;
			}
			object? obj = BatteryVisualLogicField?.GetValue(spot);
			BatteryVisualLogic val = (BatteryVisualLogic)((obj is BatteryVisualLogic) ? obj : null);
			if ((Object)(object)val != (Object)null && ((Component)val).transform.IsChildOf(((Component)spot).transform))
			{
				if (activateForVanillaStart && !((Component)val).gameObject.activeSelf)
				{
					((Component)val).gameObject.SetActive(true);
				}
				return val;
			}
			BatteryVisualLogic val2 = ((Component)spot).GetComponentsInChildren<BatteryVisualLogic>(true).FirstOrDefault();
			if ((Object)(object)val2 == (Object)null)
			{
				return null;
			}
			if (activateForVanillaStart && !((Component)val2).gameObject.activeSelf)
			{
				((Component)val2).gameObject.SetActive(true);
			}
			BatteryVisualLogicField?.SetValue(spot, val2);
			return val2;
		}

		internal static void PrepareFreshClone(InventorySpot spot, BatteryVisualLogic templateVisual)
		{
			BatteryVisualLogic val = Bind(spot, activateForVanillaStart: true);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)$"Inventory slot {((spot != null) ? new int?(spot.inventorySpotIndex + 1) : null)} has no BatteryVisualLogic child.");
				return;
			}
			BarsField?.SetValue(val, new List<GameObject>());
			ResetCloneTargets(val, templateVisual);
			ResetSprings(val, GetTargetScale(val, templateVisual));
		}

		internal static void Refresh(InventorySpot spot)
		{
			BatteryVisualLogic val = Bind(spot, activateForVanillaStart: false);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			ItemEquippable currentItem = spot.CurrentItem;
			ItemBattery val2 = (((Object)(object)currentItem == (Object)null) ? null : ((Component)currentItem).GetComponent<ItemBattery>());
			if (!((Object)(object)val2 == (Object)null))
			{
				val.itemBattery = val2;
				if (!((Component)val).gameObject.activeSelf)
				{
					((Component)val).gameObject.SetActive(true);
				}
				val.ResetOutro();
				val.BatteryBarsSet();
				val.BatteryBarsUpdate(-1, true);
			}
		}

		private static void ResetCloneTargets(BatteryVisualLogic visual, BatteryVisualLogic templateVisual)
		{
			//IL_000c: 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_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)visual == (Object)null))
			{
				Vector3 targetPosition = GetTargetPosition(visual, templateVisual);
				float targetScale = GetTargetScale(visual, templateVisual);
				float fieldValue = GetFieldValue(TargetRotationOriginalField, templateVisual, 0f);
				((Component)visual).transform.localPosition = targetPosition;
				((Component)visual).transform.localScale = new Vector3(targetScale, targetScale, targetScale);
				((Component)visual).transform.localRotation = Quaternion.Euler(0f, 0f, fieldValue);
				TargetPositionField?.SetValue(visual, targetPosition);
				TargetPositionOriginalField?.SetValue(visual, targetPosition);
				TargetScaleField?.SetValue(visual, targetScale);
				TargetScaleOriginalField?.SetValue(visual, targetScale);
				TargetRotationField?.SetValue(visual, fieldValue);
				TargetRotationOriginalField?.SetValue(visual, fieldValue);
				DoOutroField?.SetValue(visual, false);
			}
		}

		private static Vector3 GetTargetPosition(BatteryVisualLogic visual, BatteryVisualLogic templateVisual)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			Vector3 fieldValue = GetFieldValue<Vector3>(TargetPositionOriginalField, templateVisual, ((Component)visual).transform.localPosition);
			if (float.IsNaN(fieldValue.x) || float.IsInfinity(fieldValue.x))
			{
				return ((Component)visual).transform.localPosition;
			}
			return fieldValue;
		}

		private static float GetTargetScale(BatteryVisualLogic visual, BatteryVisualLogic templateVisual)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			float fieldValue = GetFieldValue(TargetScaleOriginalField, templateVisual, 0f);
			if (fieldValue > 0.001f)
			{
				return fieldValue;
			}
			fieldValue = GetFieldValue(TargetScaleField, templateVisual, 0f);
			if (fieldValue > 0.001f)
			{
				return fieldValue;
			}
			fieldValue = Mathf.Max(((Component)visual).transform.localScale.x, ((Component)visual).transform.localScale.y);
			if (!(fieldValue > 0.001f))
			{
				return 0.5f;
			}
			return fieldValue;
		}

		private static T GetFieldValue<T>(FieldInfo field, object instance, T fallback)
		{
			if (field == null || instance == null)
			{
				return fallback;
			}
			object value = field.GetValue(instance);
			if (value is T)
			{
				return (T)value;
			}
			return fallback;
		}

		private static void ResetSprings(BatteryVisualLogic visual, float targetScale)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0045: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			//IL_008e: 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_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			SpringFloat val = new SpringFloat
			{
				damping = 0.4f,
				speed = 30f
			};
			SpringFloatLastPositionField?.SetValue(val, targetScale);
			SpringScaleField?.SetValue(visual, val);
			SpringFloat val2 = new SpringFloat
			{
				damping = 0.3f,
				speed = 40f
			};
			SpringFloatLastPositionField?.SetValue(val2, 0f);
			SpringRotationField?.SetValue(visual, val2);
			SpringVector3 val3 = new SpringVector3
			{
				damping = 0.35f,
				speed = 30f
			};
			SpringVectorLastPositionField?.SetValue(val3, ((Component)visual).transform.localPosition);
			SpringPositionField?.SetValue(visual, val3);
		}
	}
	internal static class InventoryUiBuilder
	{
		private static readonly FieldInfo AllChildrenField = AccessTools.Field(typeof(SemiUI), "allChildren");

		private static readonly FieldInfo CurrentStateField = AccessTools.Field(typeof(InventorySpot), "currentState");

		private static readonly FieldInfo CurrentItemBackingField = AccessTools.Field(typeof(InventorySpot), "<CurrentItem>k__BackingField");

		private static readonly FieldInfo StateStartField = AccessTools.Field(typeof(InventorySpot), "stateStart");

		internal static void Rebuild(InventoryUI inventoryUi)
		{
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)inventoryUi == (Object)null)
			{
				return;
			}
			int effectiveSlotCount = Plugin.EffectiveSlotCount;
			if (effectiveSlotCount <= 3)
			{
				return;
			}
			try
			{
				Transform transform = ((Component)inventoryUi).transform;
				List<InventorySpot> list = (from spot in ((Component)transform).GetComponentsInChildren<InventorySpot>(true)
					orderby spot.inventorySpotIndex, ((Object)spot).name
					select spot).ToList();
				InventorySpot val = ((IEnumerable<InventorySpot>)list).FirstOrDefault((Func<InventorySpot, bool>)((InventorySpot spot) => spot.inventorySpotIndex == 0)) ?? list.FirstOrDefault();
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log.LogWarning((object)"Could not find an InventorySpot template in InventoryUI.");
					return;
				}
				List<GameObject> list2 = AllChildrenField?.GetValue(inventoryUi) as List<GameObject>;
				float num = CalculateSpacing(list);
				float num2 = CalculateCenterX(list, ((Component)val).transform.localPosition.x) - num * (float)(effectiveSlotCount - 1) * 0.5f;
				float y = ((Component)val).transform.localPosition.y;
				float z = ((Component)val).transform.localPosition.z;
				for (int i = 0; i < effectiveSlotCount; i++)
				{
					InventorySpot orCreateSpot = GetOrCreateSpot(transform, val, list, i);
					if (!((Object)(object)orCreateSpot == (Object)null))
					{
						ConfigureSpot(orCreateSpot, i, num2 + num * (float)i, y, z);
						GameObject gameObject = ((Component)orCreateSpot).gameObject;
						if (list2 != null && !list2.Contains(gameObject))
						{
							list2.Add(gameObject);
						}
					}
				}
				InventorySlotList.EnsureSlots(Inventory.instance, effectiveSlotCount);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"Failed to rebuild inventory slots: {arg}");
			}
		}

		private static InventorySpot GetOrCreateSpot(Transform root, InventorySpot template, List<InventorySpot> existingSpots, int index)
		{
			InventorySpot val = ((IEnumerable<InventorySpot>)existingSpots).FirstOrDefault((Func<InventorySpot, bool>)((InventorySpot spot) => spot.inventorySpotIndex == index));
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			Transform val2 = root.Find($"Inventory Spot {index + 1}");
			InventorySpot val3 = default(InventorySpot);
			if ((Object)(object)val2 != (Object)null && ((Component)val2).TryGetComponent<InventorySpot>(ref val3))
			{
				if (!existingSpots.Contains(val3))
				{
					existingSpots.Add(val3);
				}
				return val3;
			}
			Transform obj = Object.Instantiate<Transform>(((Component)template).transform, ((Component)template).transform.parent);
			((Object)obj).name = $"Inventory Spot {index + 1}";
			InventorySpot component = ((Component)obj).GetComponent<InventorySpot>();
			BatteryVisualLogic templateVisual = ((Component)template).GetComponentsInChildren<BatteryVisualLogic>(true).FirstOrDefault();
			InventoryBatteryBinding.PrepareFreshClone(component, templateVisual);
			ClearCopiedRuntimeState(component);
			existingSpots.Add(component);
			return component;
		}

		private static void ClearCopiedRuntimeState(InventorySpot spot)
		{
			if (!((Object)(object)spot == (Object)null))
			{
				CurrentItemBackingField?.SetValue(spot, null);
				if (CurrentStateField != null)
				{
					CurrentStateField.SetValue(spot, Enum.ToObject(CurrentStateField.FieldType, 0));
				}
				StateStartField?.SetValue(spot, true);
			}
		}

		private static void ConfigureSpot(InventorySpot spot, int index, float x, float y, float z)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			spot.inventorySpotIndex = index;
			((Object)spot).name = $"Inventory Spot {index + 1}";
			((Component)spot).transform.localPosition = new Vector3(x, y, z);
			((Component)spot).gameObject.SetActive(true);
			InventoryBatteryBinding.Bind(spot, activateForVanillaStart: false);
			string slotLabel = GetSlotLabel(index);
			if ((Object)(object)spot.noItem != (Object)null)
			{
				((TMP_Text)spot.noItem).text = slotLabel;
			}
			Transform val = ((Component)spot).transform.Find("Numbers");
			TextMeshProUGUI val2 = default(TextMeshProUGUI);
			if ((Object)(object)val != (Object)null && ((Component)val).TryGetComponent<TextMeshProUGUI>(ref val2))
			{
				((TMP_Text)val2).text = slotLabel;
			}
		}

		private static float CalculateSpacing(List<InventorySpot> spots)
		{
			List<float> list = (from spot in spots
				where spot.inventorySpotIndex >= 0 && spot.inventorySpotIndex < 3
				orderby spot.inventorySpotIndex
				select ((Component)spot).transform.localPosition.x).ToList();
			if (list.Count >= 2)
			{
				float num = Mathf.Abs(list[1] - list[0]);
				if (num > 1f)
				{
					return num;
				}
			}
			return 40f;
		}

		private static float CalculateCenterX(List<InventorySpot> spots, float fallback)
		{
			List<float> list = (from spot in spots
				where spot.inventorySpotIndex >= 0 && spot.inventorySpotIndex < 3
				orderby spot.inventorySpotIndex
				select ((Component)spot).transform.localPosition.x).ToList();
			if (list.Count >= 3)
			{
				return (list[0] + list[2]) * 0.5f;
			}
			return fallback;
		}

		private static string GetSlotLabel(int index)
		{
			if (index != 9)
			{
				return (index + 1).ToString();
			}
			return "0";
		}
	}
	internal static class ExtraSlotInput
	{
		private static readonly MethodInfo HandleInputMethod = AccessTools.Method(typeof(InventorySpot), "HandleInput", (Type[])null, (Type[])null);

		internal static void HandleExtraSlotHotkey(InventorySpot spot)
		{
			if (Plugin.ExtraHotkeys.Value && !((Object)(object)spot == (Object)null))
			{
				int inventorySpotIndex = spot.inventorySpotIndex;
				if (inventorySpotIndex >= 3 && inventorySpotIndex < Plugin.EffectiveSlotCount && WasSlotKeyPressed(inventorySpotIndex))
				{
					HandleInputMethod?.Invoke(spot, Array.Empty<object>());
				}
			}
		}

		private static bool WasSlotKeyPressed(int index)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			Keyboard current = Keyboard.current;
			if (current == null)
			{
				return false;
			}
			if (!WasPressed(current, MainKeyForSlot(index)))
			{
				if (Plugin.NumpadHotkeys.Value)
				{
					return WasPressed(current, NumpadKeyForSlot(index));
				}
				return false;
			}
			return true;
		}

		private static bool WasPressed(Keyboard keyboard, Key key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if ((int)key == 0)
			{
				return false;
			}
			KeyControl val = keyboard[key];
			if (val != null)
			{
				return ((ButtonControl)val).wasPressedThisFrame;
			}
			return false;
		}

		private static Key MainKeyForSlot(int index)
		{
			return (Key)(index switch
			{
				3 => 44, 
				4 => 45, 
				5 => 46, 
				6 => 47, 
				7 => 48, 
				8 => 49, 
				9 => 50, 
				_ => 0, 
			});
		}

		private static Key NumpadKeyForSlot(int index)
		{
			return (Key)(index switch
			{
				3 => 88, 
				4 => 89, 
				5 => 90, 
				6 => 91, 
				7 => 92, 
				8 => 93, 
				9 => 84, 
				_ => 0, 
			});
		}
	}
	[HarmonyPatch(typeof(Inventory), "Awake")]
	internal static class InventoryAwakePatch
	{
		private static void Postfix(Inventory __instance)
		{
			InventorySlotList.EnsureSlots(__instance);
		}
	}
	[HarmonyPatch(typeof(Inventory), "InventorySpotAddAtIndex")]
	internal static class InventorySpotAddAtIndexPatch
	{
		private static void Prefix(Inventory __instance, int index)
		{
			InventorySlotList.EnsureSlots(__instance, index + 1);
		}
	}
	[HarmonyPatch(typeof(InventoryUI), "Start")]
	internal static class InventoryUiStartPatch
	{
		private static void Postfix(InventoryUI __instance)
		{
			InventoryUiBuilder.Rebuild(__instance);
		}
	}
	[HarmonyPatch(typeof(InventorySpot), "Start")]
	internal static class InventorySpotStartPatch
	{
		private static void Prefix(InventorySpot __instance)
		{
			InventoryBatteryBinding.Bind(__instance, activateForVanillaStart: true);
		}

		private static void Postfix(InventorySpot __instance)
		{
			InventoryBatteryBinding.Refresh(__instance);
		}
	}
	[HarmonyPatch(typeof(InventorySpot), "Update")]
	internal static class InventorySpotUpdatePatch
	{
		private static void Postfix(InventorySpot __instance)
		{
			ExtraSlotInput.HandleExtraSlotHotkey(__instance);
		}
	}
	[HarmonyPatch(typeof(InventorySpot), "HandleInput")]
	internal static class InventorySpotHandleInputPatch
	{
		private static bool Prefix(InventorySpot __instance)
		{
			return AutoItemSwap.PrefixHandleInput(__instance);
		}
	}
	[HarmonyPatch(typeof(InventorySpot), "EquipItem")]
	internal static class InventorySpotEquipItemPatch
	{
		private static void Postfix(InventorySpot __instance)
		{
			InventoryBatteryBinding.Refresh(__instance);
		}
	}
	[HarmonyPatch(typeof(InventorySpot), "UpdateUI")]
	internal static class InventorySpotUpdateUiPatch
	{
		private static void Postfix(InventorySpot __instance)
		{
			InventoryBatteryBinding.Refresh(__instance);
		}
	}
	[HarmonyPatch(typeof(InventorySpot), "StateOccupied")]
	internal static class InventorySpotStateOccupiedPatch
	{
		private static void Prefix(InventorySpot __instance)
		{
			InventoryBatteryBinding.Bind(__instance, activateForVanillaStart: false);
		}
	}
	[HarmonyPatch(typeof(StatsManager), "PlayerInventoryUpdate")]
	internal static class StatsManagerPlayerInventoryUpdatePatch
	{
		private static void Postfix(string _steamID, string itemName, int spot)
		{
			ExtraSlotState.TrackInventoryUpdate(_steamID, itemName, spot);
		}
	}
	[HarmonyPatch(typeof(ItemEquippable), "RPC_RequestEquip")]
	internal static class ItemEquippableRequestEquipPatch
	{
		private static bool Prefix(int spotIndex)
		{
			if (IsRestoringFromItemNameLogic() && Plugin.KeepItemsInTruck.Value)
			{
				return false;
			}
			if (SemiFunc.IsMultiplayer() && Plugin.HostProtection.Value && spotIndex >= Plugin.EffectiveSlotCount)
			{
				return false;
			}
			return true;
		}

		private static bool IsRestoringFromItemNameLogic()
		{
			return new StackTrace().GetFrames()?.Any((StackFrame frame) => frame.GetMethod()?.Name == "SetItemNameLOGIC") ?? false;
		}
	}
	[HarmonyPatch(typeof(PunManager), "SetItemNameLOGIC")]
	internal static class PunManagerSetItemNameLogicPatch
	{
		private static void Postfix(string _name, int photonViewID, ItemAttributes _itemAttributes)
		{
			if (Plugin.KeepItemsInTruck.Value || string.IsNullOrEmpty(_name))
			{
				return;
			}
			try
			{
				ItemEquippable val = ResolveItemEquippable(photonViewID, _itemAttributes);
				if ((Object)(object)val == (Object)null || val.IsEquipped() || !ExtraSlotState.TryFindItemOwnerAndSpot(_name.GetHashCode(), out var owner, out var spot) || spot < 3 || spot >= Plugin.EffectiveSlotCount)
				{
					return;
				}
				int num = -1;
				if (SemiFunc.IsMultiplayer())
				{
					if ((Object)(object)owner.photonView == (Object)null)
					{
						return;
					}
					num = owner.photonView.ViewID;
				}
				val.RequestEquip(spot, num);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to restore extra-slot item '" + _name + "': " + ex.Message));
			}
		}

		private static ItemEquippable ResolveItemEquippable(int photonViewID, ItemAttributes itemAttributes)
		{
			ItemAttributes val = itemAttributes;
			if (SemiFunc.IsMultiplayer())
			{
				PhotonView val2 = PhotonView.Find(photonViewID);
				if ((Object)(object)val2 == (Object)null)
				{
					return null;
				}
				val = ((Component)val2).GetComponent<ItemAttributes>();
			}
			if (!((Object)(object)val == (Object)null))
			{
				return ((Component)val).GetComponent<ItemEquippable>();
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(MainMenuOpen), "Start")]
	internal static class MainMenuOpenStartPatch
	{
		private static void Postfix()
		{
			ExtraSlotState.Clear();
		}
	}
}