Decompiled source of InventoryExpansion v1.4.1

InventoryExpansion.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using HarmonyLib;
using InventoryExpansion;
using InventoryExpansion.Config;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using Mimic.Actors;
using ReluProtocol;
using ReluProtocol.Enum;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "InventoryExpansion", "1.4.1", "DooDesch", null)]
[assembly: MelonGame("ReLUGames", "MIMESIS")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("InventoryExpansion")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.4.1.0")]
[assembly: AssemblyInformationalVersion("1.4.1+6acff1ebbc8a8bee4fd279ac402b35ce8cd29532")]
[assembly: AssemblyProduct("InventoryExpansion")]
[assembly: AssemblyTitle("InventoryExpansion")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.4.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace InventoryExpansion
{
	public sealed class Core : MelonMod
	{
		public override void OnInitializeMelon()
		{
			InventoryExpansionPreferences.Initialize();
			((MelonBase)this).HarmonyInstance.PatchAll();
			MelonLogger.Msg("InventoryExpansion initialized. Enabled={0}", new object[1] { InventoryExpansionPreferences.Enabled });
		}
	}
}
namespace InventoryExpansion.Patches
{
	[HarmonyPatch(typeof(Hub))]
	internal static class GameConfigPatches
	{
		private static bool _slotsAlreadyExpanded;

		[HarmonyPostfix]
		[HarmonyPatch("Awake")]
		private static void Hub_Awake_Postfix(Hub __instance)
		{
			try
			{
				if (!InventoryExpansionPreferences.Enabled || _slotsAlreadyExpanded)
				{
					return;
				}
				object obj = typeof(Hub).GetField("gameConfig", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance);
				if (obj == null)
				{
					MelonLogger.Warning("InventoryExpansion: gameConfig is null, cannot adjust inventory slot count.");
					return;
				}
				object obj2 = obj.GetType().GetField("playerActor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj);
				if (obj2 == null)
				{
					MelonLogger.Warning("InventoryExpansion: gameConfig.playerActor is null, cannot adjust inventory slot count.");
					return;
				}
				FieldInfo field = obj2.GetType().GetField("maxGenericInventorySlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field == null)
				{
					MelonLogger.Warning("InventoryExpansion: PlayerActor.maxGenericInventorySlot field not found.");
					return;
				}
				int num = (int)(field.GetValue(obj2) ?? ((object)4));
				int additionalSlots = InventoryExpansionPreferences.AdditionalSlots;
				if (additionalSlots <= 0)
				{
					MelonLogger.Msg($"InventoryExpansion: keeping base inventory slots = {num}.");
					return;
				}
				int num2 = num + additionalSlots;
				field.SetValue(obj2, num2);
				_slotsAlreadyExpanded = true;
				MelonLogger.Msg($"InventoryExpansion: expanded inventory slots from {num} to {num2}.");
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"InventoryExpansion: failed to adjust inventory slot size: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(InventoryController))]
	internal static class InventoryControllerPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("Initialize")]
		private static void Initialize_Postfix(InventoryController __instance)
		{
			try
			{
				if (!InventoryExpansionPreferences.Enabled)
				{
					return;
				}
				Hub s = Hub.s;
				object obj = typeof(Hub).GetField("gameConfig", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(s);
				if (obj == null)
				{
					return;
				}
				object obj2 = obj.GetType().GetField("playerActor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj);
				if (obj2 == null)
				{
					return;
				}
				FieldInfo field = obj2.GetType().GetField("maxGenericInventorySlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field == null)
				{
					return;
				}
				int num = (int)(field.GetValue(obj2) ?? ((object)4));
				if (num <= 4)
				{
					return;
				}
				FieldInfo field2 = typeof(InventoryController).GetField("_inventorySlots", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field2 == null)
				{
					MelonLogger.Warning("InventoryExpansion: failed to find InventoryController._inventorySlots field.");
					return;
				}
				if (!(field2.GetValue(__instance) is IDictionary dictionary))
				{
					MelonLogger.Warning("InventoryExpansion: InventoryController._inventorySlots is not an IDictionary.");
					return;
				}
				for (int i = 5; i <= num; i++)
				{
					if (!dictionary.Contains(i))
					{
						dictionary[i] = null;
					}
				}
				MelonLogger.Msg($"InventoryExpansion: InventoryController extended to {num} slots.");
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"InventoryExpansion: failed to extend InventoryController slots: {arg}");
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("InvenFull")]
		private static bool InvenFull_Prefix(InventoryController __instance, ref bool __result)
		{
			try
			{
				if (!InventoryExpansionPreferences.Enabled)
				{
					return true;
				}
				Hub s = Hub.s;
				if ((Object)(object)s == (Object)null)
				{
					return true;
				}
				object obj = typeof(Hub).GetField("gameConfig", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(s);
				object obj2 = (obj?.GetType().GetField("playerActor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(obj);
				int num = (int)((obj2?.GetType().GetField("maxGenericInventorySlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(obj2) ?? ((object)4));
				if (!(typeof(InventoryController).GetField("_inventorySlots", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance) is IDictionary dictionary))
				{
					return true;
				}
				int num2 = 0;
				foreach (DictionaryEntry item in dictionary)
				{
					if (item.Value != null)
					{
						num2++;
					}
				}
				__result = num2 >= num;
				return false;
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"InventoryExpansion: InvenFull prefix failed, falling back to original. {arg}");
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(UIPrefab_Inventory))]
	internal static class InventoryUiPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("Awake")]
		private static void Awake_Postfix(UIPrefab_Inventory __instance)
		{
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_0379: Expected O, but got Unknown
			//IL_03a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_042c: Unknown result type (might be due to invalid IL or missing references)
			//IL_043b: Unknown result type (might be due to invalid IL or missing references)
			//IL_045f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0486: Unknown result type (might be due to invalid IL or missing references)
			//IL_048b: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_050a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0517: Unknown result type (might be due to invalid IL or missing references)
			//IL_0523: Unknown result type (might be due to invalid IL or missing references)
			//IL_054a: Unknown result type (might be due to invalid IL or missing references)
			//IL_054f: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0612: Unknown result type (might be due to invalid IL or missing references)
			//IL_0617: Unknown result type (might be due to invalid IL or missing references)
			//IL_0648: Unknown result type (might be due to invalid IL or missing references)
			//IL_0656: Unknown result type (might be due to invalid IL or missing references)
			//IL_0664: Unknown result type (might be due to invalid IL or missing references)
			//IL_0672: Unknown result type (might be due to invalid IL or missing references)
			//IL_0680: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!InventoryExpansionPreferences.Enabled)
				{
					return;
				}
				int val = 4;
				try
				{
					Hub s = Hub.s;
					if ((Object)(object)s != (Object)null)
					{
						object obj = typeof(Hub).GetField("gameConfig", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(s);
						object obj2 = (obj?.GetType().GetField("playerActor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(obj);
						FieldInfo fieldInfo = obj2?.GetType().GetField("maxGenericInventorySlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (fieldInfo != null)
						{
							val = (int)(fieldInfo.GetValue(obj2) ?? ((object)4));
						}
					}
				}
				catch
				{
					val = 4;
				}
				int num = Math.Min(val, 4 + InventoryExpansionPreferences.AdditionalSlots);
				FieldInfo field = typeof(UIPrefab_Inventory).GetField("inventorySlots", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field == null || !(field.GetValue(__instance) is IList { Count: var count } list) || count == 0 || count >= num)
				{
					return;
				}
				Type type = list[0].GetType();
				FieldInfo field2 = type.GetField("frame", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				FieldInfo field3 = type.GetField("image", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				FieldInfo field4 = type.GetField("stackCount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				FieldInfo field5 = type.GetField("waitEvent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field2 == null || field3 == null || field4 == null || field5 == null)
				{
					return;
				}
				object? value = field2.GetValue(list[0]);
				Image val2 = (Image)((value is Image) ? value : null);
				object? value2 = field3.GetValue(list[0]);
				Image val3 = (Image)((value2 is Image) ? value2 : null);
				object? value3 = field4.GetValue(list[0]);
				TMP_Text val4 = (TMP_Text)((value3 is TMP_Text) ? value3 : null);
				object? value4 = field5.GetValue(list[0]);
				Transform val5 = (Transform)((value4 is Transform) ? value4 : null);
				if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null || (Object)(object)val5 == (Object)null)
				{
					return;
				}
				RectTransform rectTransform = ((Graphic)val2).rectTransform;
				_ = ((Graphic)val3).rectTransform;
				_ = val4.rectTransform;
				float num2 = 0f;
				if (count > 1)
				{
					object? value5 = field2.GetValue(list[1]);
					Image val6 = (Image)((value5 is Image) ? value5 : null);
					if ((Object)(object)val6 != (Object)null)
					{
						num2 = ((Graphic)val6).rectTransform.anchoredPosition.x - rectTransform.anchoredPosition.x;
					}
				}
				if (Mathf.Approximately(num2, 0f))
				{
					num2 = rectTransform.sizeDelta.x + 10f;
				}
				float num3 = rectTransform.sizeDelta.y + 10f;
				Transform parent = ((Transform)rectTransform).parent;
				RectTransform val7 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
				for (int i = count; i < num; i++)
				{
					int index = count - 1;
					object obj4 = list[index];
					object? value6 = field2.GetValue(obj4);
					Image val8 = (Image)((value6 is Image) ? value6 : null);
					object? value7 = field3.GetValue(obj4);
					Image val9 = (Image)((value7 is Image) ? value7 : null);
					object? value8 = field4.GetValue(obj4);
					TMP_Text val10 = (TMP_Text)((value8 is TMP_Text) ? value8 : null);
					object? value9 = field5.GetValue(obj4);
					Transform val11 = (Transform)((value9 is Transform) ? value9 : null);
					if ((Object)(object)val8 == (Object)null || (Object)(object)val9 == (Object)null || (Object)(object)val10 == (Object)null || (Object)(object)val11 == (Object)null)
					{
						continue;
					}
					int num4 = i - count;
					int num5 = num4 / 8;
					int num6 = num4 % 8;
					GameObject val12 = new GameObject(((Object)((Component)val8).gameObject).name + "_Extra" + i);
					val12.transform.SetParent((Transform)(object)val7, false);
					Image val13 = val12.AddComponent<Image>();
					val13.sprite = val8.sprite;
					val13.type = val8.type;
					((Graphic)val13).material = ((Graphic)val8).material;
					((Graphic)val13).color = ((Graphic)val8).color;
					RectTransform rectTransform2 = ((Graphic)val13).rectTransform;
					rectTransform2.anchorMin = rectTransform.anchorMin;
					rectTransform2.anchorMax = rectTransform.anchorMax;
					rectTransform2.pivot = rectTransform.pivot;
					rectTransform2.sizeDelta = rectTransform.sizeDelta;
					object? value10 = field2.GetValue(list[count - 1]);
					object? obj5 = ((value10 is Image) ? value10 : null);
					float num7 = (((obj5 != null) ? ((Graphic)obj5).rectTransform : null) ?? rectTransform).anchoredPosition.x + num2;
					float y = rectTransform.anchoredPosition.y;
					float num8 = num7 - (float)num6 * num2;
					float num9 = y + (float)num5 * num3;
					rectTransform2.anchoredPosition = new Vector2(num8, num9);
					GameObject val14 = new GameObject(((Object)((Component)val9).gameObject).name + "_Extra" + i);
					val14.transform.SetParent(val12.transform, false);
					Image val15 = val14.AddComponent<Image>();
					val15.sprite = val9.sprite;
					val15.type = val9.type;
					((Graphic)val15).color = ((Graphic)val9).color;
					((Graphic)val15).material = ((Graphic)val9).material;
					RectTransform rectTransform3 = ((Graphic)val15).rectTransform;
					RectTransform rectTransform4 = ((Graphic)val9).rectTransform;
					rectTransform3.anchorMin = rectTransform4.anchorMin;
					rectTransform3.anchorMax = rectTransform4.anchorMax;
					rectTransform3.pivot = rectTransform4.pivot;
					rectTransform3.sizeDelta = rectTransform4.sizeDelta;
					rectTransform3.anchoredPosition = rectTransform4.anchoredPosition;
					GameObject val16 = new GameObject(((Object)((Component)val10).gameObject).name + "_Extra" + i);
					val16.transform.SetParent(val12.transform, false);
					Component obj6 = val16.AddComponent(((object)val10).GetType());
					TMP_Text val17 = (TMP_Text)(object)((obj6 is TMP_Text) ? obj6 : null);
					if ((Object)(object)val17 == (Object)null)
					{
						MelonLogger.Error("[InventoryExpansion][UI] Failed to create TMP_Text for extra slot " + i + "; skipping this slot.");
						Object.Destroy((Object)(object)val12);
						continue;
					}
					val17.text = string.Empty;
					val17.font = val10.font;
					val17.fontSize = val10.fontSize;
					val17.alignment = val10.alignment;
					((Graphic)val17).color = ((Graphic)val10).color;
					BackpackPanelPatch.ApplyStackPlacement(val17, rectTransform, val4);
					GameObject val18 = new GameObject(((Object)((Component)val11).gameObject).name + "_Extra" + i);
					val18.transform.SetParent(val12.transform, false);
					RectTransform val19 = val18.AddComponent<RectTransform>();
					RectTransform val20 = (RectTransform)(object)((val11 is RectTransform) ? val11 : null);
					if ((Object)(object)val20 != (Object)null)
					{
						val19.anchorMin = val20.anchorMin;
						val19.anchorMax = val20.anchorMax;
						val19.pivot = val20.pivot;
						val19.sizeDelta = val20.sizeDelta;
						val19.anchoredPosition = val20.anchoredPosition;
					}
					object obj7 = Activator.CreateInstance(type);
					field2.SetValue(obj7, val13);
					field3.SetValue(obj7, val15);
					field4.SetValue(obj7, val17);
					field5.SetValue(obj7, ((Component)val19).transform);
					list.Add(obj7);
				}
				MelonLogger.Msg("[InventoryExpansion][UI] Extended inventorySlots from " + count + " to " + list.Count + " visual slots.");
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[InventoryExpansion][UI] Awake postfix failed while extending slots: " + ex);
			}
		}
	}
	[HarmonyPatch]
	internal static class InventoryDebugControllerPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(InventoryController), "Initialize")]
		private static void InventoryController_Initialize_Postfix(InventoryController __instance)
		{
			try
			{
				if (typeof(InventoryController).GetField("_inventorySlots", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance) is IDictionary dictionary)
				{
					string text = string.Join(",", dictionary.Keys.Cast<object>());
					MelonLogger.Msg("[InventoryExpansion][Debug] InventoryController.Initialize: slot dict count = " + dictionary.Count + ", keys = [" + text + "]");
				}
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][Debug] InventoryController.Initialize debug failed: {arg}");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(InventoryController), "GetInventoryInfos")]
		private static void InventoryController_GetInventoryInfos_Postfix(Dictionary<int, ItemInfo> __result)
		{
			try
			{
				if (__result == null)
				{
					MelonLogger.Msg("[InventoryExpansion][Debug] GetInventoryInfos: result is null.");
					return;
				}
				int count = __result.Count;
				int num = ((__result.Count > 0) ? __result.Keys.Max() : 0);
				MelonLogger.Msg($"[InventoryExpansion][Debug] GetInventoryInfos: {count} entries, max key = {num}.");
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][Debug] GetInventoryInfos debug failed: {arg}");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(InventoryController), "HandleAddItem")]
		private unsafe static void InventoryController_HandleAddItem_Postfix(InventoryController __instance, MsgErrorCode __result, int addedSlotIndex)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Invalid comparison between Unknown and I4
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				IDictionary dictionary = typeof(InventoryController).GetField("_inventorySlots", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance) as IDictionary;
				int num = dictionary?.Count ?? (-1);
				string text = ((dictionary != null) ? string.Join(",", dictionary.Keys.Cast<object>()) : "null");
				if ((int)__result == 20000012)
				{
					MelonLogger.Msg("[InventoryExpansion][Debug] HandleAddItem: INVENTORY FULL. slotCount=" + num + ", keys=[" + text + "]");
				}
				else if ((int)__result == 0)
				{
					MelonLogger.Msg("[InventoryExpansion][Debug] HandleAddItem: Success, addedSlotIndex=" + addedSlotIndex + ", slotCount=" + num + ".");
				}
				else
				{
					MelonLogger.Msg("[InventoryExpansion][Debug] HandleAddItem: Result=" + ((object)(*(MsgErrorCode*)(&__result))/*cast due to .constrained prefix*/).ToString() + ", addedSlotIndex=" + addedSlotIndex + ", slotCount=" + num + ".");
				}
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][Debug] HandleAddItem debug failed: {arg}");
			}
		}
	}
	[HarmonyPatch]
	internal static class InventoryDebugClientPatches
	{
		private static MethodBase TargetMethod()
		{
			try
			{
				Type nestedType = typeof(ProtoActor).GetNestedType("Inventory", BindingFlags.Public | BindingFlags.NonPublic);
				if (nestedType == null)
				{
					MelonLogger.Error("[InventoryExpansion][Debug] Failed to find nested type ProtoActor.Inventory for ResolveInventoryInfos debug patch.");
					return null;
				}
				MethodInfo? method = nestedType.GetMethod("ResolveInventoryInfos", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (method == null)
				{
					MelonLogger.Error("[InventoryExpansion][Debug] Failed to find ResolveInventoryInfos method on ProtoActor.Inventory.");
				}
				return method;
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[InventoryExpansion][Debug] TargetMethod for ResolveInventoryInfos failed: " + ex);
				return null;
			}
		}

		[HarmonyPostfix]
		private static void Inventory_ResolveInventoryInfos_Postfix(object __instance, Dictionary<int, ItemInfo> serverItemInfos, int? currentServerSlotIndex)
		{
			try
			{
				if (__instance == null)
				{
					return;
				}
				Type type = __instance.GetType();
				int num = (int)(type.GetField("slotSize", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance) ?? ((object)0));
				IList list = type.GetField("slotItems", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance) as IList;
				int num2 = 0;
				if (list != null)
				{
					foreach (object item in list)
					{
						if (item != null)
						{
							num2++;
						}
					}
				}
				int num3 = serverItemInfos?.Count ?? 0;
				int num4 = ((num3 > 0) ? serverItemInfos.Keys.Max() : 0);
				string text = (currentServerSlotIndex.HasValue ? currentServerSlotIndex.Value.ToString() : "null");
				MelonLogger.Msg("[InventoryExpansion][Debug] ResolveInventoryInfos: slotSize=" + num + ", serverItems=" + num3 + ", serverMaxKey=" + num4 + ", localNonNullItems=" + num2 + ", currentServerSlotIndex=" + text + ".");
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[InventoryExpansion][Debug] ResolveInventoryInfos debug failed: " + ex);
			}
		}
	}
	[HarmonyPatch]
	internal static class BackpackPanelPatch
	{
		private static GameObject _rootObj;

		private static GameObject _canvasObj;

		private static RectTransform _backpackPanel;

		private static bool _slotsMoved;

		private static bool _backpackFullyVisible;

		private static Sprite _backpackSprite;

		private static object _animationCoroutine;

		private static float _panelHeight;

		private static float _initialPanelY;

		private static TMP_Text _keyHintText;

		private static int _savedStandardSlot;

		private static FieldInfo _uimanField;

		internal static bool IsBackpackFullyVisible => _backpackFullyVisible;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(UIPrefab_Inventory), "Awake")]
		private static void UIPrefab_Inventory_Awake_Postfix(UIPrefab_Inventory __instance)
		{
			try
			{
				if (InventoryExpansionPreferences.Enabled)
				{
					BackpackSceneChangeHandler.Initialize();
					if ((Object)(object)_backpackPanel != (Object)null)
					{
						CleanupMovedSlots();
						_slotsMoved = false;
						MelonCoroutines.Start(MoveSlotsToPanelCoroutine(__instance));
					}
					else
					{
						CreateRoot();
						CreateUI();
						MelonCoroutines.Start(MoveSlotsToPanelCoroutine(__instance));
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[InventoryExpansion][BackpackPanel] Failed to create backpack panel: " + ex);
			}
		}

		private static void CreateRoot()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			if (!((Object)(object)_rootObj != (Object)null))
			{
				_rootObj = new GameObject("InventoryExpansion_Root");
				Object.DontDestroyOnLoad((Object)(object)_rootObj);
			}
		}

		private static void CreateUI()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_canvasObj != (Object)null)
			{
				return;
			}
			if ((Object)(object)_rootObj == (Object)null)
			{
				CreateRoot();
			}
			_canvasObj = new GameObject("InventoryExpansion_Canvas");
			_canvasObj.transform.SetParent(_rootObj.transform, false);
			Canvas val = _canvasObj.AddComponent<Canvas>();
			val.renderMode = (RenderMode)0;
			CanvasScaler obj = _canvasObj.AddComponent<CanvasScaler>();
			obj.uiScaleMode = (ScaleMode)0;
			obj.scaleFactor = 1f;
			Canvas val2 = null;
			Canvas[] array = Object.FindObjectsByType<Canvas>((FindObjectsSortMode)0);
			foreach (Canvas val3 in array)
			{
				if ((int)val3.renderMode == 0 && ((Object)val3).name.Contains("Canvas") && !((Object)val3).name.Contains("InventoryExpansion"))
				{
					val2 = val3;
					break;
				}
			}
			if ((Object)(object)val2 != (Object)null)
			{
				val.sortingOrder = val2.sortingOrder + 1;
			}
			_canvasObj.AddComponent<GraphicRaycaster>();
			GameObject val4 = new GameObject("InventoryExpansion_BackpackPanel");
			val4.transform.SetParent(_canvasObj.transform, false);
			Image val5 = val4.AddComponent<Image>();
			LoadBackpackSprite();
			if ((Object)(object)_backpackSprite != (Object)null)
			{
				val5.sprite = _backpackSprite;
				val5.type = (Type)0;
				val5.preserveAspect = false;
			}
			else
			{
				((Graphic)val5).color = new Color(0f, 0f, 0f, 0.8f);
			}
			((Graphic)val5).raycastTarget = false;
			_backpackPanel = val4.GetComponent<RectTransform>();
			_backpackPanel.anchorMin = new Vector2(1f, 0f);
			_backpackPanel.anchorMax = new Vector2(1f, 0f);
			_backpackPanel.pivot = new Vector2(1f, 0f);
			_backpackPanel.sizeDelta = new Vector2(450f, 200f);
			_backpackPanel.anchoredPosition = new Vector2(-40f, 40f);
			_initialPanelY = 40f;
			try
			{
				CreateKeyHintText(val4);
			}
			catch (Exception arg)
			{
				MelonLogger.Warning($"[InventoryExpansion][BackpackPanel] Failed to create key hint text during UI creation: {arg}");
			}
			_backpackFullyVisible = false;
			((Component)_backpackPanel).gameObject.SetActive(false);
		}

		private static void LoadBackpackSprite()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				string text = Path.Combine(Path.GetDirectoryName(typeof(BackpackPanelPatch).Assembly.Location), "Assets", "Backpack.png");
				if (!File.Exists(text))
				{
					MelonLogger.Warning("[InventoryExpansion][BackpackPanel] Backpack asset not found at: " + text);
					return;
				}
				byte[] array = File.ReadAllBytes(text);
				Texture2D val = new Texture2D(2, 2);
				bool flag = false;
				try
				{
					flag = ImageConversion.LoadImage(val, array);
				}
				catch
				{
					try
					{
						flag = ImageConversion.LoadImage(val, array);
					}
					catch
					{
					}
				}
				if (!flag)
				{
					MelonLogger.Error("[InventoryExpansion][BackpackPanel] Failed to load Backpack.png as texture");
					Object.Destroy((Object)(object)val);
				}
				else
				{
					_backpackSprite = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
					MelonLogger.Msg($"[InventoryExpansion][BackpackPanel] Loaded Backpack sprite: {((Texture)val).width}x{((Texture)val).height}");
				}
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][BackpackPanel] Failed to load Backpack sprite: {arg}");
			}
		}

		internal static void ToggleBackpack()
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_backpackPanel == (Object)null) && !IsInLoadingScreen() && !IsGamePaused())
			{
				bool targetVisible = !_backpackFullyVisible;
				if (_animationCoroutine != null)
				{
					MelonCoroutines.Stop(_animationCoroutine);
					_animationCoroutine = null;
				}
				((Component)_backpackPanel).gameObject.SetActive(true);
				if (_panelHeight == 0f)
				{
					_panelHeight = _backpackPanel.sizeDelta.y;
				}
				if (_initialPanelY == 0f)
				{
					_initialPanelY = 40f;
				}
				_animationCoroutine = MelonCoroutines.Start(AnimateBackpackVisibility(targetVisible));
			}
		}

		internal static void HandleCursorHandoff(ProtoActor avatar, bool opening)
		{
			try
			{
				if ((Object)(object)avatar == (Object)null || (opening && !InventoryExpansionPreferences.SelectBackpackSlotOnOpen) || (!opening && !InventoryExpansionPreferences.RestoreStandardSlotOnClose))
				{
					return;
				}
				object obj = InventorySelectionHelper.GetActorInventoryField()?.GetValue(avatar);
				if (obj == null)
				{
					return;
				}
				Type type = obj.GetType();
				FieldInfo slotSizeField = InventorySelectionHelper.GetSlotSizeField(type);
				FieldInfo selectedSlotIndexField = InventorySelectionHelper.GetSelectedSlotIndexField(type);
				MethodInfo selectSlotMethod = InventorySelectionHelper.GetSelectSlotMethod(type);
				if (slotSizeField == null || selectedSlotIndexField == null || selectSlotMethod == null || (int)(slotSizeField.GetValue(obj) ?? ((object)4)) <= 4)
				{
					return;
				}
				int num = (int)(selectedSlotIndexField.GetValue(obj) ?? ((object)0));
				if (opening)
				{
					if (num >= 0 && num <= 3)
					{
						_savedStandardSlot = num;
					}
					selectSlotMethod.Invoke(obj, new object[1] { 4 });
				}
				else
				{
					int num2 = Mathf.Clamp(_savedStandardSlot, 0, 3);
					selectSlotMethod.Invoke(obj, new object[1] { num2 });
				}
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][BackpackPanel] Cursor handoff failed: {arg}");
			}
		}

		internal static bool IsGamePaused()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Time.timeScale <= 0.01f)
				{
					return true;
				}
				if ((int)Cursor.lockState == 0 && Cursor.visible && IsInGame())
				{
					return true;
				}
				return false;
			}
			catch
			{
				return false;
			}
		}

		internal static bool IsInGame()
		{
			try
			{
				GameMainBase main = Hub.Main;
				ProtoActor val = ((main != null) ? main.GetMyAvatar() : null);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				return val.AmIAvatar();
			}
			catch (Exception arg)
			{
				MelonLogger.Warning($"[InventoryExpansion][BackpackPanel] IsInGame check failed: {arg}");
				return false;
			}
		}

		private static bool IsInLoadingScreen()
		{
			try
			{
				if ((Object)(object)Hub.s == (Object)null)
				{
					return true;
				}
				GameMainBase main = Hub.Main;
				ProtoActor val = ((main != null) ? main.GetMyAvatar() : null);
				if ((Object)(object)val == (Object)null)
				{
					return true;
				}
				return !val.AmIAvatar();
			}
			catch
			{
				return false;
			}
		}

		internal static void HideBackpackCompletely()
		{
			if ((Object)(object)_backpackPanel != (Object)null && (Object)(object)((Component)_backpackPanel).gameObject != (Object)null)
			{
				((Component)_backpackPanel).gameObject.SetActive(false);
			}
		}

		private static void CleanupMovedSlots()
		{
			if ((Object)(object)_backpackPanel == (Object)null)
			{
				return;
			}
			for (int num = ((Transform)_backpackPanel).childCount - 1; num >= 0; num--)
			{
				Transform child = ((Transform)_backpackPanel).GetChild(num);
				if ((Object)(object)child != (Object)null && ((Object)child).name.StartsWith("InventoryExpansion_SlotContainer_"))
				{
					Object.Destroy((Object)(object)((Component)child).gameObject);
				}
			}
		}

		internal static bool IsLoadingScreenActive()
		{
			try
			{
				Hub s = Hub.s;
				if ((Object)(object)s == (Object)null)
				{
					return false;
				}
				if (_uimanField == null)
				{
					_uimanField = typeof(Hub).GetField("<uiman>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
				}
				object? obj = _uimanField?.GetValue(s);
				UIPrefab_Scene_Loading val = ((UIManager)(((obj is UIManager) ? obj : null)?)).ui_sceneloading;
				return (Object)(object)val != (Object)null && ((Behaviour)val).isActiveAndEnabled;
			}
			catch
			{
				return false;
			}
		}

		internal static void EnsurePeekIfHidden()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_backpackPanel == (Object)null) && _slotsMoved && _animationCoroutine == null && !((Component)_backpackPanel).gameObject.activeSelf)
			{
				if (_panelHeight == 0f)
				{
					_panelHeight = _backpackPanel.sizeDelta.y;
				}
				if (_initialPanelY == 0f)
				{
					_initialPanelY = 40f;
				}
				float num = _initialPanelY - _panelHeight * 0.75f;
				_backpackPanel.anchoredPosition = new Vector2(_backpackPanel.anchoredPosition.x, num);
				_backpackFullyVisible = false;
				((Component)_backpackPanel).gameObject.SetActive(true);
				UpdateKeyHintVisibility();
			}
		}

		private static IEnumerator AnimateBackpackVisibility(bool targetVisible)
		{
			if ((Object)(object)_backpackPanel == (Object)null)
			{
				yield break;
			}
			if (_panelHeight == 0f)
			{
				_panelHeight = _backpackPanel.sizeDelta.y;
			}
			if (_initialPanelY == 0f)
			{
				_initialPanelY = 40f;
			}
			float num = _initialPanelY - _panelHeight * 0.75f;
			float initialPanelY = _initialPanelY;
			Vector2 startPos = _backpackPanel.anchoredPosition;
			Vector2 targetPos = (targetVisible ? new Vector2(startPos.x, initialPanelY) : new Vector2(startPos.x, num));
			if (Mathf.Abs(startPos.y - targetPos.y) < 1f)
			{
				_backpackFullyVisible = targetVisible;
				UpdateKeyHintVisibility();
				_animationCoroutine = null;
				yield break;
			}
			float elapsed = 0f;
			((Component)_backpackPanel).gameObject.SetActive(true);
			while (elapsed < 0.3f)
			{
				if ((Object)(object)_backpackPanel == (Object)null || (Object)(object)((Component)_backpackPanel).gameObject == (Object)null)
				{
					yield break;
				}
				elapsed += Time.deltaTime;
				float num2 = Mathf.Clamp01(elapsed / 0.3f);
				num2 = 1f - Mathf.Pow(1f - num2, 3f);
				_backpackPanel.anchoredPosition = Vector2.Lerp(startPos, targetPos, num2);
				UpdateKeyHintVisibility();
				yield return null;
			}
			if ((Object)(object)_backpackPanel != (Object)null && (Object)(object)((Component)_backpackPanel).gameObject != (Object)null)
			{
				_backpackPanel.anchoredPosition = targetPos;
				_backpackFullyVisible = targetVisible;
				UpdateKeyHintVisibility();
			}
			_animationCoroutine = null;
		}

		private static void CreateKeyHintText(GameObject parent)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)parent == (Object)null)
				{
					MelonLogger.Warning("[InventoryExpansion][BackpackPanel] Cannot create key hint text: parent is null");
					return;
				}
				Type type = null;
				TMP_FontAsset val = null;
				try
				{
					TMP_Text[] array = Object.FindObjectsByType<TMP_Text>((FindObjectsSortMode)0);
					if (array != null && array.Length != 0)
					{
						TMP_Text[] array2 = array;
						foreach (TMP_Text val2 in array2)
						{
							if ((Object)(object)val2 != (Object)null && !((object)val2).Equals((object?)null) && (Object)(object)val2.font != (Object)null)
							{
								type = ((object)val2).GetType();
								val = val2.font;
								break;
							}
						}
					}
				}
				catch (Exception arg)
				{
					MelonLogger.Warning($"[InventoryExpansion][BackpackPanel] Error finding TMP_Text type: {arg}");
				}
				if (type == null)
				{
					type = typeof(TMP_Text);
				}
				GameObject val3 = new GameObject("InventoryExpansion_KeyHint");
				if ((Object)(object)val3 == (Object)null)
				{
					MelonLogger.Warning("[InventoryExpansion][BackpackPanel] Failed to create key hint GameObject");
					return;
				}
				val3.transform.SetParent(parent.transform, false);
				val3.transform.SetAsLastSibling();
				Component obj = val3.AddComponent(type);
				_keyHintText = (TMP_Text)(object)((obj is TMP_Text) ? obj : null);
				if ((Object)(object)_keyHintText == (Object)null)
				{
					MelonLogger.Warning("[InventoryExpansion][BackpackPanel] Failed to add TMP_Text component");
					Object.Destroy((Object)(object)val3);
					return;
				}
				if ((Object)(object)val != (Object)null)
				{
					_keyHintText.font = val;
				}
				else
				{
					TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset;
					if ((Object)(object)defaultFontAsset != (Object)null)
					{
						_keyHintText.font = defaultFontAsset;
					}
				}
				_keyHintText.text = ((object)InventoryExpansionPreferences.BackpackKey/*cast due to .constrained prefix*/).ToString();
				_keyHintText.fontSize = 24f;
				_keyHintText.alignment = (TextAlignmentOptions)514;
				((Graphic)_keyHintText).color = new Color(1f, 1f, 1f, 1f);
				_keyHintText.fontStyle = (FontStyles)1;
				Outline val4 = val3.AddComponent<Outline>();
				if ((Object)(object)val4 != (Object)null)
				{
					((Shadow)val4).effectColor = new Color(0f, 0f, 0f, 1f);
					((Shadow)val4).effectDistance = new Vector2(2f, 2f);
				}
				if ((Object)(object)_keyHintText.rectTransform == (Object)null)
				{
					MelonLogger.Warning("[InventoryExpansion][BackpackPanel] Key hint text has no RectTransform");
					return;
				}
				RectTransform rectTransform = _keyHintText.rectTransform;
				rectTransform.anchorMin = new Vector2(0.5f, 1f);
				rectTransform.anchorMax = new Vector2(0.5f, 1f);
				rectTransform.pivot = new Vector2(0.5f, 0.5f);
				rectTransform.sizeDelta = new Vector2(60f, 35f);
				rectTransform.anchoredPosition = new Vector2(0f, -95f);
				((Graphic)_keyHintText).raycastTarget = false;
				((Component)_keyHintText).gameObject.SetActive(true);
				MelonLogger.Msg("[InventoryExpansion][BackpackPanel] Key hint text created successfully");
			}
			catch (Exception arg2)
			{
				MelonLogger.Error($"[InventoryExpansion][BackpackPanel] Failed to create key hint text: {arg2}");
				_keyHintText = null;
			}
		}

		private static void UpdateKeyHintVisibility()
		{
			if ((Object)(object)_keyHintText == (Object)null || (Object)(object)((Component)_keyHintText).gameObject == (Object)null)
			{
				return;
			}
			try
			{
				((Component)_keyHintText).gameObject.SetActive(true);
			}
			catch
			{
			}
		}

		private static (float padding, float paddingTop, float paddingBottom) GetPaddingForSlotCount(int additionalSlots)
		{
			return additionalSlots switch
			{
				4 => (padding: 90f, paddingTop: 130f, paddingBottom: 90f), 
				9 => (padding: 160f, paddingTop: 200f, paddingBottom: 140f), 
				_ => (padding: 200f, paddingTop: 240f, paddingBottom: 180f), 
			};
		}

		private static (float horizontal, float top) GetSlotPaddingForSlotCount(int additionalSlots)
		{
			return additionalSlots switch
			{
				4 => (horizontal: 90f, top: 170f), 
				9 => (horizontal: 160f, top: 260f), 
				_ => (horizontal: 200f, top: 320f), 
			};
		}

		internal static void ApplyStackPlacement(TMP_Text stackText, RectTransform templateFrameRT, TMP_Text templateStack)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)stackText == (Object)null) && !((Object)(object)templateFrameRT == (Object)null))
			{
				RectTransform rectTransform = stackText.rectTransform;
				Vector2 sizeDelta = templateFrameRT.sizeDelta;
				rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
				rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
				rectTransform.pivot = new Vector2(0.5f, 0.5f);
				rectTransform.sizeDelta = new Vector2(sizeDelta.x * 0.85f, sizeDelta.y * 0.85f);
				rectTransform.anchoredPosition = Vector2.zero;
				stackText.alignment = (TextAlignmentOptions)1028;
				if ((Object)(object)templateStack != (Object)null)
				{
					stackText.fontSize = templateStack.fontSize;
				}
			}
		}

		private static IEnumerator MoveSlotsToPanelCoroutine(UIPrefab_Inventory inventoryUI)
		{
			yield return null;
			try
			{
				if ((Object)(object)_backpackPanel == (Object)null || _slotsMoved)
				{
					yield break;
				}
				FieldInfo field = typeof(UIPrefab_Inventory).GetField("inventorySlots", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field == null)
				{
					MelonLogger.Error("[InventoryExpansion][BackpackPanel] Could not find inventorySlots field!");
				}
				else
				{
					if (!(field.GetValue(inventoryUI) is IList { Count: >4 } list))
					{
						yield break;
					}
					Type type = list[0].GetType();
					FieldInfo field2 = type.GetField("frame", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field2 == null)
					{
						MelonLogger.Error("[InventoryExpansion][BackpackPanel] Could not find frame field in slot!");
						yield break;
					}
					object obj = list[0];
					object? value = field2.GetValue(obj);
					Image val = (Image)((value is Image) ? value : null);
					if ((Object)(object)val == (Object)null)
					{
						MelonLogger.Error("[InventoryExpansion][BackpackPanel] Could not get first frame!");
						yield break;
					}
					RectTransform rectTransform = ((Graphic)val).rectTransform;
					float x = rectTransform.sizeDelta.x;
					float y = rectTransform.sizeDelta.y;
					x *= 0.5f;
					y *= 0.5f;
					Image val2 = null;
					Transform parent = ((Transform)rectTransform).parent;
					if ((Object)(object)parent != (Object)null)
					{
						for (int i = 0; i < parent.childCount; i++)
						{
							Transform child = parent.GetChild(i);
							if (((Object)child).name.Contains("InvenBG") || ((Object)child).name.Contains("BG"))
							{
								val2 = ((Component)child).GetComponent<Image>();
								if ((Object)(object)val2 != (Object)null)
								{
									break;
								}
							}
						}
					}
					float num = 10f;
					int num2 = list.Count - 4;
					int num3 = num2 switch
					{
						4 => 2, 
						9 => 3, 
						16 => 4, 
						_ => Mathf.CeilToInt(Mathf.Sqrt((float)num2)), 
					};
					int num4 = Mathf.CeilToInt((float)num2 / (float)num3);
					float num5 = (float)num3 * x + (float)(num3 + 1) * num;
					float num6 = (float)num4 * y + (float)(num4 + 1) * num;
					(float padding, float paddingTop, float paddingBottom) paddingForSlotCount = GetPaddingForSlotCount(num2);
					float item = paddingForSlotCount.padding;
					float item2 = paddingForSlotCount.paddingTop;
					float item3 = paddingForSlotCount.paddingBottom;
					float num7 = num5 + item * 2f;
					float num8 = num6 + item2 + item3;
					_backpackPanel.sizeDelta = new Vector2(num7, num8);
					FieldInfo field3 = type.GetField("image", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					FieldInfo field4 = type.GetField("stackCount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					object? obj2 = field3?.GetValue(obj);
					Image val3 = (Image)((obj2 is Image) ? obj2 : null);
					object? obj3 = field4?.GetValue(obj);
					TMP_Text templateStack = (TMP_Text)((obj3 is TMP_Text) ? obj3 : null);
					RectTransform val4 = ((val3 != null) ? ((Graphic)val3).rectTransform : null);
					for (int j = 4; j < list.Count; j++)
					{
						object obj4 = list[j];
						object? value2 = field2.GetValue(obj4);
						Image val5 = (Image)((value2 is Image) ? value2 : null);
						if ((Object)(object)val5 == (Object)null)
						{
							continue;
						}
						RectTransform rectTransform2 = ((Graphic)val5).rectTransform;
						if (!((Object)((Component)val5).gameObject).name.Contains("_Extra"))
						{
							continue;
						}
						GameObject val6 = new GameObject("InventoryExpansion_SlotContainer_" + j);
						val6.transform.SetParent((Transform)(object)_backpackPanel, false);
						RectTransform obj5 = val6.AddComponent<RectTransform>();
						int num9 = j - 4;
						int num10 = num9 / num3;
						int num11 = num9 % num3;
						obj5.anchorMin = new Vector2(0f, 1f);
						obj5.anchorMax = new Vector2(0f, 1f);
						obj5.pivot = new Vector2(0f, 1f);
						(float horizontal, float top) slotPaddingForSlotCount = GetSlotPaddingForSlotCount(num2);
						float item4 = slotPaddingForSlotCount.horizontal;
						float item5 = slotPaddingForSlotCount.top;
						float num12 = item4 + num + (float)num11 * (x + num);
						float num13 = 0f - (item5 + num + (float)num10 * (y + num));
						obj5.anchoredPosition = new Vector2(num12, num13);
						obj5.sizeDelta = new Vector2(x, y);
						if ((Object)(object)val2 != (Object)null)
						{
							GameObject val7 = new GameObject("InvenBG_Extra" + j);
							val7.transform.SetParent(val6.transform, false);
							Image obj6 = val7.AddComponent<Image>();
							obj6.sprite = val2.sprite;
							((Graphic)obj6).material = ((Graphic)val2).material;
							((Graphic)obj6).color = ((Graphic)val2).color;
							obj6.type = val2.type;
							obj6.preserveAspect = val2.preserveAspect;
							obj6.fillMethod = val2.fillMethod;
							obj6.fillAmount = val2.fillAmount;
							obj6.fillCenter = val2.fillCenter;
							obj6.fillClockwise = val2.fillClockwise;
							obj6.fillOrigin = val2.fillOrigin;
							((Graphic)obj6).raycastTarget = ((Graphic)val2).raycastTarget;
							((MaskableGraphic)obj6).maskable = ((MaskableGraphic)val2).maskable;
							RectTransform rectTransform3 = ((Graphic)obj6).rectTransform;
							RectTransform rectTransform4 = ((Graphic)val2).rectTransform;
							rectTransform3.anchorMin = rectTransform4.anchorMin;
							rectTransform3.anchorMax = rectTransform4.anchorMax;
							rectTransform3.pivot = rectTransform4.pivot;
							rectTransform3.sizeDelta = rectTransform4.sizeDelta * 0.5f;
							rectTransform3.anchoredPosition = rectTransform4.anchoredPosition * 0.5f;
						}
						((Transform)rectTransform2).SetParent(val6.transform, false);
						RectTransform rectTransform5 = ((Graphic)val).rectTransform;
						rectTransform2.anchorMin = new Vector2(0.5f, 0.5f);
						rectTransform2.anchorMax = new Vector2(0.5f, 0.5f);
						rectTransform2.pivot = rectTransform5.pivot;
						rectTransform2.sizeDelta = rectTransform5.sizeDelta;
						rectTransform2.anchoredPosition = Vector2.zero;
						((Transform)rectTransform2).localScale = new Vector3(0.5f, 0.5f, 1f);
						val5.sprite = val.sprite;
						((Graphic)val5).material = ((Graphic)val).material;
						((Graphic)val5).color = ((Graphic)val).color;
						val5.type = val.type;
						val5.preserveAspect = val.preserveAspect;
						val5.fillMethod = val.fillMethod;
						val5.fillAmount = val.fillAmount;
						val5.fillCenter = val.fillCenter;
						val5.fillClockwise = val.fillClockwise;
						val5.fillOrigin = val.fillOrigin;
						if (field3 != null && (Object)(object)val3 != (Object)null && (Object)(object)val4 != (Object)null)
						{
							object? value3 = field3.GetValue(obj4);
							Image val8 = (Image)((value3 is Image) ? value3 : null);
							if ((Object)(object)val8 != (Object)null)
							{
								RectTransform rectTransform6 = ((Graphic)val8).rectTransform;
								val8.sprite = val3.sprite;
								((Graphic)val8).material = ((Graphic)val3).material;
								((Graphic)val8).color = ((Graphic)val3).color;
								val8.type = val3.type;
								val8.preserveAspect = val3.preserveAspect;
								val8.fillMethod = val3.fillMethod;
								val8.fillAmount = val3.fillAmount;
								val8.fillCenter = val3.fillCenter;
								val8.fillClockwise = val3.fillClockwise;
								val8.fillOrigin = val3.fillOrigin;
								rectTransform6.anchorMin = val4.anchorMin;
								rectTransform6.anchorMax = val4.anchorMax;
								rectTransform6.pivot = val4.pivot;
								rectTransform6.sizeDelta = val4.sizeDelta;
								rectTransform6.anchoredPosition = val4.anchoredPosition;
							}
						}
						if (field4 != null)
						{
							object? value4 = field4.GetValue(obj4);
							TMP_Text val9 = (TMP_Text)((value4 is TMP_Text) ? value4 : null);
							if ((Object)(object)val9 != (Object)null)
							{
								ApplyStackPlacement(val9, rectTransform5, templateStack);
							}
						}
					}
					_slotsMoved = true;
					_panelHeight = _backpackPanel.sizeDelta.y;
					if (_initialPanelY == 0f)
					{
						_initialPanelY = 40f;
					}
					float num14 = _initialPanelY - _panelHeight * 0.75f;
					_backpackPanel.anchoredPosition = new Vector2(_backpackPanel.anchoredPosition.x, num14);
					_backpackFullyVisible = false;
					UpdateKeyHintVisibility();
					MelonLogger.Msg("[InventoryExpansion][BackpackPanel] Moved {0} additional slots to panel. Panel size: {1}", new object[2] { num2, _backpackPanel.sizeDelta });
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Error("[InventoryExpansion][BackpackPanel] Failed to move slots to panel: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(ProtoActor), "Update")]
	internal static class BackpackInputUpdatePatch
	{
		private static bool wasKeyPressedLastFrame;

		[HarmonyPostfix]
		private unsafe static void Update_Postfix(ProtoActor __instance)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: 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_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!InventoryExpansionPreferences.Enabled || !__instance.AmIAvatar())
				{
					return;
				}
				if (BackpackPanelPatch.IsLoadingScreenActive())
				{
					BackpackPanelPatch.HideBackpackCompletely();
					wasKeyPressedLastFrame = false;
					return;
				}
				if (BackpackPanelPatch.IsGamePaused())
				{
					if (BackpackPanelPatch.IsBackpackFullyVisible)
					{
						BackpackPanelPatch.ToggleBackpack();
					}
					else
					{
						BackpackPanelPatch.HideBackpackCompletely();
					}
					wasKeyPressedLastFrame = false;
					return;
				}
				BackpackPanelPatch.EnsurePeekIfHidden();
				Keyboard current = Keyboard.current;
				if (current == null)
				{
					wasKeyPressedLastFrame = false;
					return;
				}
				KeyCode backpackKey = InventoryExpansionPreferences.BackpackKey;
				Key val = (Key)0;
				try
				{
					val = (Key)Enum.Parse(typeof(Key), ((object)(*(KeyCode*)(&backpackKey))/*cast due to .constrained prefix*/).ToString());
				}
				catch
				{
					MelonLogger.Warning($"[InventoryExpansion][BackpackPanel] Could not convert KeyCode {backpackKey} to Input System Key");
					return;
				}
				bool isPressed = ((ButtonControl)current[val]).isPressed;
				bool num = isPressed && !wasKeyPressedLastFrame;
				wasKeyPressedLastFrame = isPressed;
				if (num)
				{
					bool opening = !BackpackPanelPatch.IsBackpackFullyVisible;
					BackpackPanelPatch.ToggleBackpack();
					BackpackPanelPatch.HandleCursorHandoff(__instance, opening);
				}
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][BackpackPanel] Update postfix failed: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(ProtoActor))]
	internal static class BackpackMovementSpeedPatch
	{
		private static FieldInfo _netSyncActorDataField;

		private static bool _fieldInitialized;

		private static long _originalMoveSpeedWalk;

		private static long _originalMoveSpeedRun;

		private static bool _speedReduced;

		[HarmonyPostfix]
		[HarmonyPatch("Update")]
		private static void Update_Postfix(ProtoActor __instance)
		{
			try
			{
				if (!InventoryExpansionPreferences.Enabled)
				{
					RestoreMoveSpeed(__instance);
				}
				else
				{
					if (!__instance.AmIAvatar())
					{
						return;
					}
					if (!_fieldInitialized)
					{
						_netSyncActorDataField = typeof(ProtoActor).GetField("netSyncActorData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						_fieldInitialized = true;
					}
					if (_netSyncActorDataField == null)
					{
						return;
					}
					object value = _netSyncActorDataField.GetValue(__instance);
					if (value == null)
					{
						return;
					}
					FieldInfo field = value.GetType().GetField("MoveSpeedWalk", BindingFlags.Instance | BindingFlags.Public);
					FieldInfo field2 = value.GetType().GetField("MoveSpeedRun", BindingFlags.Instance | BindingFlags.Public);
					if (field == null || field2 == null)
					{
						return;
					}
					if (BackpackPanelPatch.IsBackpackFullyVisible && InventoryExpansionPreferences.ReduceMovementSpeed)
					{
						if (!_speedReduced)
						{
							_originalMoveSpeedWalk = (long)(field.GetValue(value) ?? ((object)350L));
							_originalMoveSpeedRun = (long)(field2.GetValue(value) ?? ((object)700L));
							_speedReduced = true;
						}
						field.SetValue(value, (long)((float)_originalMoveSpeedWalk * 0.5f));
						field2.SetValue(value, (long)((float)_originalMoveSpeedRun * 0.5f));
					}
					else
					{
						RestoreMoveSpeed(value, field, field2);
					}
				}
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][Movement] Movement speed patch failed: {arg}");
			}
		}

		private static void RestoreMoveSpeed(ProtoActor instance)
		{
			if (!_speedReduced || _netSyncActorDataField == null)
			{
				return;
			}
			object value = _netSyncActorDataField.GetValue(instance);
			if (value != null)
			{
				FieldInfo field = value.GetType().GetField("MoveSpeedWalk", BindingFlags.Instance | BindingFlags.Public);
				FieldInfo field2 = value.GetType().GetField("MoveSpeedRun", BindingFlags.Instance | BindingFlags.Public);
				if (field != null && field2 != null)
				{
					RestoreMoveSpeed(value, field, field2);
				}
			}
		}

		private static void RestoreMoveSpeed(object netSyncActorData, FieldInfo moveSpeedWalkField, FieldInfo moveSpeedRunField)
		{
			if (_speedReduced && _originalMoveSpeedWalk > 0 && _originalMoveSpeedRun > 0)
			{
				moveSpeedWalkField.SetValue(netSyncActorData, _originalMoveSpeedWalk);
				moveSpeedRunField.SetValue(netSyncActorData, _originalMoveSpeedRun);
				_speedReduced = false;
			}
		}
	}
	[HarmonyPatch(typeof(ProtoActor), "OnDestroy")]
	internal static class BackpackProtoActorDestroyPatch
	{
		[HarmonyPostfix]
		private static void OnDestroy_Postfix(ProtoActor __instance)
		{
			try
			{
				if (InventoryExpansionPreferences.Enabled && __instance.AmIAvatar())
				{
					BackpackSceneChangeHandler.HideBackpack();
				}
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][BackpackPanel] ProtoActor OnDestroy patch failed: {arg}");
			}
		}
	}
	internal static class BackpackSceneChangeHandler
	{
		private static bool _initialized;

		internal static void Initialize()
		{
			if (!_initialized)
			{
				SceneManager.activeSceneChanged += OnActiveSceneChanged;
				_initialized = true;
			}
		}

		private static void OnActiveSceneChanged(Scene previousScene, Scene newScene)
		{
			try
			{
				if (InventoryExpansionPreferences.Enabled && !IsInGame())
				{
					HideBackpack();
				}
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][BackpackPanel] Scene change handler failed: {arg}");
			}
		}

		private static bool IsInGame()
		{
			try
			{
				GameMainBase main = Hub.Main;
				ProtoActor val = ((main != null) ? main.GetMyAvatar() : null);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				return val.AmIAvatar();
			}
			catch
			{
				return false;
			}
		}

		internal static void HideBackpack()
		{
			if (BackpackPanelPatch.IsBackpackFullyVisible)
			{
				BackpackPanelPatch.ToggleBackpack();
			}
			else
			{
				BackpackPanelPatch.HideBackpackCompletely();
			}
		}
	}
	internal static class InventorySelectionHelper
	{
		internal static Type GetInventoryType()
		{
			return typeof(ProtoActor).GetNestedType("Inventory", BindingFlags.Public | BindingFlags.NonPublic);
		}

		internal static FieldInfo GetSelectedSlotIndexField(Type inventoryType)
		{
			return inventoryType?.GetField("selectedSlotIndex", BindingFlags.Instance | BindingFlags.NonPublic);
		}

		internal static FieldInfo GetSlotSizeField(Type inventoryType)
		{
			return inventoryType?.GetField("slotSize", BindingFlags.Instance | BindingFlags.NonPublic);
		}

		internal static FieldInfo GetActorInventoryField()
		{
			return typeof(ProtoActor).GetField("inventory", BindingFlags.Instance | BindingFlags.NonPublic);
		}

		internal static MethodInfo GetSelectSlotMethod(Type inventoryType)
		{
			return inventoryType?.GetMethod("SelectSlot", BindingFlags.Instance | BindingFlags.NonPublic);
		}
	}
	[HarmonyPatch]
	internal static class InventorySelectionNextPatches
	{
		private static MethodBase TargetMethod()
		{
			return InventorySelectionHelper.GetInventoryType()?.GetMethod("SelectNextSlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}

		[HarmonyPrefix]
		private static bool SelectNextSlot_Prefix(object __instance)
		{
			return InventorySelectionCommon.HandleSlotSelection(__instance, forward: true);
		}
	}
	[HarmonyPatch]
	internal static class InventorySelectionPreviousPatches
	{
		private static MethodBase TargetMethod()
		{
			return InventorySelectionHelper.GetInventoryType()?.GetMethod("SelectPreviousSlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}

		[HarmonyPrefix]
		private static bool SelectPreviousSlot_Prefix(object __instance)
		{
			return InventorySelectionCommon.HandleSlotSelection(__instance, forward: false);
		}
	}
	internal static class InventorySelectionCommon
	{
		internal static bool HandleSlotSelection(object __instance, bool forward)
		{
			try
			{
				if (!InventoryExpansionPreferences.Enabled)
				{
					return true;
				}
				if (__instance == null)
				{
					return true;
				}
				Type type = __instance.GetType();
				FieldInfo selectedSlotIndexField = InventorySelectionHelper.GetSelectedSlotIndexField(type);
				FieldInfo slotSizeField = InventorySelectionHelper.GetSlotSizeField(type);
				if (selectedSlotIndexField == null || slotSizeField == null)
				{
					return true;
				}
				int num = (int)(selectedSlotIndexField.GetValue(__instance) ?? ((object)0));
				int num2 = (int)(slotSizeField.GetValue(__instance) ?? ((object)4));
				int num3;
				int num4;
				if (BackpackPanelPatch.IsBackpackFullyVisible)
				{
					num3 = 4;
					num4 = num2 - 1;
				}
				else
				{
					num3 = 0;
					num4 = 3;
				}
				if (num3 > num4 || num < num3 || num > num4)
				{
					MethodInfo method = type.GetMethod("SelectSlot", BindingFlags.Instance | BindingFlags.NonPublic);
					if (method != null)
					{
						method.Invoke(__instance, new object[1] { num3 });
						return false;
					}
					return true;
				}
				int num5;
				if (forward)
				{
					num5 = num + 1;
					if (num5 > num4)
					{
						num5 = num3;
					}
				}
				else
				{
					num5 = num - 1;
					if (num5 < num3)
					{
						num5 = num4;
					}
				}
				MethodInfo method2 = type.GetMethod("SelectSlot", BindingFlags.Instance | BindingFlags.NonPublic);
				if (method2 != null)
				{
					method2.Invoke(__instance, new object[1] { num5 });
					return false;
				}
				return true;
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][Selection] Slot selection prefix failed: {arg}");
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(InventoryController))]
	internal static class InventoryPickupPatches
	{
		private static FieldInfo _slotsField;

		private static MethodInfo _onAddItemByLooting;

		private static bool _reflectionInitialized;

		private static void EnsureReflection()
		{
			if (!_reflectionInitialized)
			{
				_reflectionInitialized = true;
				_slotsField = typeof(InventoryController).GetField("_inventorySlots", BindingFlags.Instance | BindingFlags.NonPublic);
				_onAddItemByLooting = typeof(InventoryController).GetMethod("OnAddItemByLooting", BindingFlags.Instance | BindingFlags.NonPublic);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("HandleAddItem")]
		private static bool HandleAddItem_Prefix(InventoryController __instance, ItemElement itemElement, ref int addedSlotIndex, bool sync, bool byLooting, ref MsgErrorCode __result)
		{
			try
			{
				if (!InventoryExpansionPreferences.Enabled || !InventoryExpansionPreferences.FillBackpackFirst)
				{
					return true;
				}
				if (!BackpackPanelPatch.IsBackpackFullyVisible)
				{
					return true;
				}
				EnsureReflection();
				if (_slotsField == null)
				{
					return true;
				}
				if (!(_slotsField.GetValue(__instance) is IDictionary dictionary))
				{
					return true;
				}
				int currentInventorySlot = __instance.CurrentInventorySlot;
				if (dictionary.Contains(currentInventorySlot) && dictionary[currentInventorySlot] == null)
				{
					return true;
				}
				int num = -1;
				foreach (object key in dictionary.Keys)
				{
					if (key is int num2 && num2 >= 5 && dictionary[key] == null && (num < 0 || num2 < num))
					{
						num = num2;
					}
				}
				if (num < 0)
				{
					return true;
				}
				if (byLooting && _onAddItemByLooting != null)
				{
					_onAddItemByLooting.Invoke(__instance, new object[1] { itemElement });
				}
				__instance.AddInvenItem(num, itemElement, sync);
				addedSlotIndex = num;
				__result = (MsgErrorCode)0;
				return false;
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"[InventoryExpansion][Pickup] HandleAddItem prefix failed, falling back to original: {arg}");
				return true;
			}
		}
	}
}
namespace InventoryExpansion.Config
{
	internal static class InventoryExpansionPreferences
	{
		private const string CategoryId = "InventoryExpansion";

		private static MelonPreferences_Category _category;

		private static MelonPreferences_Entry<bool> _enabled;

		private static MelonPreferences_Entry<int> _additionalSlots;

		private static MelonPreferences_Entry<string> _backpackKey;

		private static MelonPreferences_Entry<bool> _reduceMovementSpeed;

		private static MelonPreferences_Entry<bool> _selectBackpackSlotOnOpen;

		private static MelonPreferences_Entry<bool> _restoreStandardSlotOnClose;

		private static MelonPreferences_Entry<bool> _fillBackpackFirst;

		internal static bool Enabled => _enabled.Value;

		internal static int AdditionalSlots
		{
			get
			{
				if (_additionalSlots == null)
				{
					return 4;
				}
				int value = _additionalSlots.Value;
				int num = ClampToValidBackpackSize(value);
				if (num != value)
				{
					_additionalSlots.Value = num;
				}
				return num;
			}
		}

		internal static KeyCode BackpackKey
		{
			get
			{
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				if (_backpackKey == null)
				{
					return (KeyCode)99;
				}
				string value = _backpackKey.Value;
				if (string.IsNullOrEmpty(value))
				{
					return (KeyCode)99;
				}
				if (Enum.TryParse<KeyCode>(value, ignoreCase: true, out KeyCode result))
				{
					return result;
				}
				return (KeyCode)99;
			}
		}

		internal static bool ReduceMovementSpeed => _reduceMovementSpeed?.Value ?? true;

		internal static bool SelectBackpackSlotOnOpen => _selectBackpackSlotOnOpen?.Value ?? true;

		internal static bool RestoreStandardSlotOnClose => _restoreStandardSlotOnClose?.Value ?? true;

		internal static bool FillBackpackFirst => _fillBackpackFirst?.Value ?? true;

		internal static void Initialize()
		{
			if (_category == null)
			{
				_category = MelonPreferences.CreateCategory("InventoryExpansion", "InventoryExpansion");
				_enabled = CreateEntry("Enabled", defaultValue: true, "Enabled", "Enable InventoryExpansion functionality. When disabled, the mod will not modify game behavior.");
				_additionalSlots = CreateEntry("AdditionalSlots", 4, "Additional Inventory Slots", "Number of extra inventory slots to add on top of the game's default inventory size. Valid values: 4, 9, or 16 (for square grids: 2x2, 3x3, or 4x4). Other values will be rounded to the nearest valid option.");
				_backpackKey = CreateEntry("BackpackKey", "C", "Backpack Toggle Key", "Key to toggle backpack visibility. Press to switch between standard inventory and backpack.");
				_reduceMovementSpeed = CreateEntry("ReduceMovementSpeed", defaultValue: true, "Reduce Movement Speed", "When enabled, player movement speed is reduced to 50% while the backpack is fully open.");
				_selectBackpackSlotOnOpen = CreateEntry("SelectBackpackSlotOnOpen", defaultValue: true, "Select Backpack Slot On Open", "When opening the backpack, move the selected slot to the first backpack slot.");
				_restoreStandardSlotOnClose = CreateEntry("RestoreStandardSlotOnClose", defaultValue: true, "Restore Standard Slot On Close", "When closing the backpack, return the selected slot to the standard inventory (the slot that was selected before opening).");
				_fillBackpackFirst = CreateEntry("FillBackpackFirst", defaultValue: true, "Fill Backpack First", "While the backpack is open, picked-up items fill the backpack slots before the standard inventory. Takes effect only when you are the host or in single-player.");
			}
		}

		private static MelonPreferences_Entry<T> CreateEntry<T>(string identifier, T defaultValue, string displayName, string description = null)
		{
			if (_category == null)
			{
				throw new InvalidOperationException("Preference category not initialized.");
			}
			return _category.CreateEntry<T>(identifier, defaultValue, displayName, description, false, false, (ValueValidator)null, (string)null);
		}

		private static int ClampToValidBackpackSize(int value)
		{
			if (value <= 0)
			{
				return 4;
			}
			if (value <= 6)
			{
				return 4;
			}
			if (value <= 12)
			{
				return 9;
			}
			return 16;
		}
	}
}