Decompiled source of MoreHotbarSlots v1.0.0

BepInEx\plugins\DaanDeReus-MoreHotbarSlots\MoreHotbarSlots.dll

Decompiled a week 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 System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyCompany("DaanDeReus")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MoreHotbarSlots")]
[assembly: AssemblyTitle("MoreHotbarSlots")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 MoreHotbarSlots
{
	internal static class ExtraSlotMemory
	{
		private const float RestoreTimeout = 30f;

		private static readonly Dictionary<int, string> itemBySlot = new Dictionary<int, string>();

		private static InventorySpot? restoredForSpot;

		internal static void Remember(int slotIndex, ItemEquippable item)
		{
			ItemAttributes component = ((Component)item).GetComponent<ItemAttributes>();
			if ((Object)(object)component == (Object)null || string.IsNullOrEmpty(component.instanceName))
			{
				return;
			}
			foreach (KeyValuePair<int, string> item2 in new List<KeyValuePair<int, string>>(itemBySlot))
			{
				if (item2.Value == component.instanceName)
				{
					itemBySlot.Remove(item2.Key);
				}
			}
			itemBySlot[slotIndex] = component.instanceName;
		}

		internal static void Forget(int slotIndex)
		{
			itemBySlot.Remove(slotIndex);
		}

		internal static void Clear()
		{
			itemBySlot.Clear();
		}

		internal static void OnSpotRegistered(Inventory inventory)
		{
			if (itemBySlot.Count == 0)
			{
				return;
			}
			int value = MoreHotbarSlotsConfig.SlotCount.Value;
			for (int i = 0; i < value; i++)
			{
				if ((Object)(object)InventoryPatch.GetSpot(inventory, i) == (Object)null)
				{
					return;
				}
			}
			InventorySpot spot = InventoryPatch.GetSpot(inventory, 0);
			if (!((Object)(object)spot == (Object)(object)restoredForSpot))
			{
				restoredForSpot = spot;
				((MonoBehaviour)MoreHotbarSlots.Instance).StartCoroutine(RestoreWhenReady());
			}
		}

		private static IEnumerator RestoreWhenReady()
		{
			Dictionary<int, string> pending = new Dictionary<int, string>(itemBySlot);
			float deadline = Time.time + 30f;
			while (pending.Count > 0 && Time.time < deadline)
			{
				yield return (object)new WaitForSeconds(0.5f);
				Inventory instance = Inventory.instance;
				PlayerAvatar instance2 = PlayerAvatar.instance;
				if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || (Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated)
				{
					continue;
				}
				int num = (SemiFunc.IsMultiplayer() ? instance2.photonView.ViewID : (-1));
				ItemAttributes[] array = Object.FindObjectsOfType<ItemAttributes>();
				foreach (ItemAttributes val in array)
				{
					foreach (KeyValuePair<int, string> item in pending)
					{
						if (!(val.instanceName != item.Value))
						{
							ItemEquippable component = ((Component)val).GetComponent<ItemEquippable>();
							InventorySpot spot = InventoryPatch.GetSpot(instance, item.Key);
							if ((Object)(object)component != (Object)null && (Object)(object)spot != (Object)null && !spot.IsOccupied() && !component.IsEquipped())
							{
								component.RequestEquip(item.Key, num);
								MoreHotbarSlots.Logger.LogInfo((object)$"Restored {item.Value} to slot {item.Key + 1}.");
							}
							pending.Remove(item.Key);
							break;
						}
					}
				}
			}
			foreach (KeyValuePair<int, string> item2 in pending)
			{
				MoreHotbarSlots.Logger.LogWarning((object)$"Could not find {item2.Value} to restore to slot {item2.Key + 1}.");
			}
		}
	}
	[HarmonyPatch]
	internal static class ExtraSlotPersistencePatch
	{
		private static ItemEquippable? destroyingItem;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(InventorySpot), "EquipItem")]
		private static void EquipItem_Postfix(InventorySpot __instance, ItemEquippable item)
		{
			if (__instance.inventorySpotIndex < 3 || (Object)(object)item == (Object)null)
			{
				return;
			}
			try
			{
				ExtraSlotMemory.Remember(__instance.inventorySpotIndex, item);
			}
			catch (Exception ex)
			{
				MoreHotbarSlots.Logger.LogError((object)("EquipItem postfix failed: " + ex.Message));
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(InventorySpot), "UnequipItem")]
		private static void UnequipItem_Prefix(InventorySpot __instance)
		{
			if (__instance.inventorySpotIndex >= 3)
			{
				ItemEquippable currentItem = __instance.CurrentItem;
				if (!((Object)(object)currentItem == (Object)null) && !((Object)(object)currentItem == (Object)(object)destroyingItem))
				{
					ExtraSlotMemory.Forget(__instance.inventorySpotIndex);
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ItemEquippable), "OnDestroy")]
		private static void ItemOnDestroy_Prefix(ItemEquippable __instance)
		{
			destroyingItem = __instance;
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(ItemEquippable), "OnDestroy")]
		private static void ItemOnDestroy_Finalizer()
		{
			destroyingItem = null;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(RunManager), "ResetProgress")]
		private static void ResetProgress_Postfix()
		{
			ExtraSlotMemory.Clear();
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(RunManager), "LeaveToMainMenu")]
		private static void LeaveToMainMenu_Postfix()
		{
			ExtraSlotMemory.Clear();
		}
	}
	[HarmonyPatch(typeof(Inventory))]
	internal static class InventoryPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch("InventorySpotAddAtIndex")]
		private static void InventorySpotAddAtIndex_Prefix(Inventory __instance, int index)
		{
			EnsureCapacity(__instance, index + 1);
		}

		[HarmonyPostfix]
		[HarmonyPatch("InventorySpotAddAtIndex")]
		private static void InventorySpotAddAtIndex_Postfix(Inventory __instance)
		{
			ExtraSlotMemory.OnSpotRegistered(__instance);
		}

		private static void EnsureCapacity(Inventory inventory, int count)
		{
			List<InventorySpot> inventorySpots = inventory.inventorySpots;
			while (inventorySpots.Count < count)
			{
				inventorySpots.Add(null);
			}
		}

		internal static InventorySpot? GetSpot(Inventory inventory, int index)
		{
			List<InventorySpot> inventorySpots = inventory.inventorySpots;
			if (index < 0 || index >= inventorySpots.Count)
			{
				return null;
			}
			return inventorySpots[index];
		}
	}
	[HarmonyPatch(typeof(InventoryUI))]
	internal static class InventoryUIPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch("LayoutSetup")]
		private static void LayoutSetup_Prefix(InventoryUI __instance)
		{
			try
			{
				AddExtraSpots(__instance);
			}
			catch (Exception arg)
			{
				MoreHotbarSlots.Logger.LogError((object)$"Failed to add extra hotbar slots: {arg}");
			}
		}

		private static void AddExtraSpots(InventoryUI ui)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			int value = MoreHotbarSlotsConfig.SlotCount.Value;
			List<InventorySpot> list = (from s in ((Component)ui).GetComponentsInChildren<InventorySpot>(true)
				orderby s.inventorySpotIndex
				select s).ToList();
			if (list.Count < 2)
			{
				MoreHotbarSlots.Logger.LogWarning((object)$"Expected the vanilla hotbar slots under InventoryUI, found {list.Count}. Skipping.");
			}
			else
			{
				if (list.Count >= value)
				{
					return;
				}
				InventorySpot val = list[0];
				InventorySpot val2 = list[list.Count - 1];
				Vector3 val3 = ((Component)list[1]).transform.localPosition - ((Component)val).transform.localPosition;
				int count = list.Count;
				for (int num = count; num < value; num++)
				{
					GameObject val4 = Object.Instantiate<GameObject>(((Component)val2).gameObject, ((Component)val2).transform.parent);
					((Object)val4).name = $"{((Object)((Component)val2).gameObject).name} ({num + 1})";
					InventorySpot component = val4.GetComponent<InventorySpot>();
					component.inventorySpotIndex = num;
					val4.transform.localPosition = ((Component)val).transform.localPosition + val3 * (float)num;
					RelabelSpot(val4, val2.inventorySpotIndex, num);
					list.Add(component);
				}
				Vector3 val5 = -val3 * (float)(value - count) / 2f;
				foreach (InventorySpot item in list)
				{
					Transform transform = ((Component)item).transform;
					transform.localPosition += val5;
				}
				MoreHotbarSlots.Logger.LogInfo((object)$"Hotbar expanded to {value} slots.");
			}
		}

		private static void RelabelSpot(GameObject clone, int templateIndex, int index)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			KeyCode keyForSlot = MoreHotbarSlotsConfig.GetKeyForSlot(index);
			string newValue = (((int)keyForSlot == 0) ? "" : MoreHotbarSlotsConfig.GetKeyLabel(keyForSlot));
			string text = $"[inventory{templateIndex + 1}]";
			InputGlyphText[] componentsInChildren = clone.GetComponentsInChildren<InputGlyphText>(true);
			foreach (InputGlyphText val in componentsInChildren)
			{
				if (!string.IsNullOrEmpty(val.template) && val.template.Contains(text))
				{
					val.SetTemplate(val.template.Replace(text, newValue));
				}
			}
		}
	}
	[BepInPlugin("DaanDeReus.MoreHotbarSlots", "MoreHotbarSlots", "1.0.0")]
	public class MoreHotbarSlots : BaseUnityPlugin
	{
		internal static MoreHotbarSlots Instance { get; private set; }

		internal static ManualLogSource Logger => Instance._logger;

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		internal Harmony? Harmony { get; set; }

		private void Awake()
		{
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			MoreHotbarSlotsConfig.Bind(((BaseUnityPlugin)this).Config);
			Patch();
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded with {MoreHotbarSlotsConfig.SlotCount.Value} hotbar slots!");
		}

		internal void Patch()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0025: Expected O, but got Unknown
			if (Harmony == null)
			{
				Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll();
		}

		internal void Unpatch()
		{
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void Update()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			Inventory instance = Inventory.instance;
			if ((Object)(object)instance == (Object)null || MoreHotbarSlotsConfig.ExtraSlotCount <= 0 || !SemiFunc.NoTextInputsActive() || ((Object)(object)ChatManager.instance != (Object)null && ChatManager.instance.chatActive))
			{
				return;
			}
			PlayerAvatar instance2 = PlayerAvatar.instance;
			if ((Object)(object)instance2 == (Object)null || instance2.isDisabled || instance2.deadSet)
			{
				return;
			}
			for (int i = 3; i < MoreHotbarSlotsConfig.SlotCount.Value; i++)
			{
				KeyCode keyForSlot = MoreHotbarSlotsConfig.GetKeyForSlot(i);
				if ((int)keyForSlot == 0 || !Input.GetKeyDown(keyForSlot))
				{
					continue;
				}
				InventorySpot spot = InventoryPatch.GetSpot(instance, i);
				if (!((Object)(object)spot == (Object)null))
				{
					try
					{
						spot.HandleInput();
					}
					catch (Exception ex)
					{
						Logger.LogError((object)$"Slot {i + 1} input failed: {ex.Message}");
					}
				}
			}
		}
	}
	internal static class MoreHotbarSlotsConfig
	{
		internal const int VanillaSlotCount = 3;

		internal const int MaxSlotCount = 10;

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

		internal static ConfigEntry<string> ExtraSlotKeys { get; private set; } = null;

		internal static List<KeyCode> ParsedExtraSlotKeys { get; } = new List<KeyCode>();

		internal static int ExtraSlotCount => SlotCount.Value - 3;

		internal static void Bind(ConfigFile config)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			SlotCount = config.Bind<int>("General", "SlotCount", 6, new ConfigDescription("Total number of hotbar slots (vanilla is 3). Takes effect on the next level load.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 10), Array.Empty<object>()));
			ExtraSlotKeys = config.Bind<string>("General", "ExtraSlotKeys", "4, 5, 6, 7, 8, 9, 0", "Comma-separated keys for slot 4 and up, in order. A single digit means the number row key; anything else is a Unity KeyCode name (e.g. F1, Keypad4, Mouse3). Slots 1-3 keep the game's own keybinds.");
			ExtraSlotKeys.SettingChanged += delegate
			{
				RefreshExtraSlotKeys();
			};
			RefreshExtraSlotKeys();
		}

		private static void RefreshExtraSlotKeys()
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			ParsedExtraSlotKeys.Clear();
			string[] array = ExtraSlotKeys.Value.Split(',');
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.Length != 0)
				{
					if (text2.Length == 1 && char.IsDigit(text2[0]))
					{
						text2 = "Alpha" + text2;
					}
					if (Enum.TryParse<KeyCode>(text2, ignoreCase: true, out KeyCode result))
					{
						ParsedExtraSlotKeys.Add(result);
						continue;
					}
					MoreHotbarSlots.Logger.LogWarning((object)$"Invalid key '{text.Trim()}' in ExtraSlotKeys, slot {3 + ParsedExtraSlotKeys.Count + 1} has no key.");
					ParsedExtraSlotKeys.Add((KeyCode)0);
				}
			}
		}

		internal static KeyCode GetKeyForSlot(int slotIndex)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			int num = slotIndex - 3;
			if (num < 0 || num >= ParsedExtraSlotKeys.Count)
			{
				return (KeyCode)0;
			}
			return ParsedExtraSlotKeys[num];
		}

		internal unsafe static string GetKeyLabel(KeyCode key)
		{
			string text = ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString();
			if (text.StartsWith("Alpha"))
			{
				return text.Substring("Alpha".Length);
			}
			if (text.StartsWith("Keypad") && text.Length == "Keypad".Length + 1)
			{
				return text.Substring("Keypad".Length);
			}
			return text;
		}
	}
}