Decompiled source of ExtraQuickslotsUnbound v1.0.0

ExtraQuickslotsUnbound.dll

Decompiled 13 hours ago
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace ExtraQuickslotsUnbound;

[BepInPlugin("com.spencer4792.extraquickslotsunbound", "ExtraQuickslotsUnbound", "1.0.0")]
public class ExtraQuickslotsPlugin : BaseUnityPlugin
{
	public const string GUID = "com.spencer4792.extraquickslotsunbound";

	public const string NAME = "ExtraQuickslotsUnbound";

	public const string VERSION = "1.0.0";

	public const int EXTRA = 8;

	internal static ManualLogSource Log;

	internal static int s_baseCount = -1;

	private static readonly KeyCode[] s_slotKeys;

	private static readonly string[] s_slotLabels;

	private static readonly int[] s_labelToVanillaSlot;

	private static readonly int[] s_vanillaSlotToLabel;

	private static readonly Type s_inputType;

	private static readonly MethodInfo s_getKey;

	private static readonly MethodInfo s_getKeyDown;

	private static GameObject s_uiRow;

	private static string s_uiRowOwner;

	private static Component[] s_uiDisplays;

	private static float s_nextEnforce;

	private static readonly Type s_panelType;

	private static readonly Type s_displayType;

	private static readonly FieldInfo s_refSlotField;

	private static readonly FieldInfo s_refQuickSlotField;

	private static readonly MethodInfo s_setQuickSlot;

	internal static bool ShiftHeld => Key((KeyCode)304) || Key((KeyCode)303);

	private static bool CtrlHeld => Key((KeyCode)306) || Key((KeyCode)305);

	internal static bool CtrlHeldPublic => CtrlHeld;

	internal void Awake()
	{
		Log = ((BaseUnityPlugin)this).Logger;
		try
		{
			Init();
		}
		catch (Exception ex)
		{
			Log.LogError((object)("Startup failed: " + ex));
		}
	}

	private void Init()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		new Harmony("com.spencer4792.extraquickslotsunbound").PatchAll(typeof(ExtraQuickslotsPlugin).Assembly);
		Log.LogMessage((object)"ExtraQuickslotsUnbound 1.0.0 ready.");
	}

	private static bool Key(KeyCode k)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		return s_getKey != null && (bool)s_getKey.Invoke(null, new object[1] { k });
	}

	private static bool KeyDown(KeyCode k)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		return s_getKeyDown != null && (bool)s_getKeyDown.Invoke(null, new object[1] { k });
	}

	private void Update()
	{
		try
		{
			if (KeyDown((KeyCode)291))
			{
				DumpQuickslotUi();
			}
			MaintainUiRow();
			if ((Object)(object)s_uiRow != (Object)null && Time.unscaledTime > s_nextEnforce)
			{
				s_nextEnforce = Time.unscaledTime + 1f;
				Character val = ((!((Object)(object)CharacterManager.Instance != (Object)null)) ? null : CharacterManager.Instance.GetFirstLocalCharacter());
				if ((Object)(object)val != (Object)null)
				{
					EnforceBindings(val);
				}
			}
			if (!ShiftHeld && !CtrlHeld)
			{
				return;
			}
			Character val2 = ((!((Object)(object)CharacterManager.Instance != (Object)null)) ? null : CharacterManager.Instance.GetFirstLocalCharacter());
			if ((Object)(object)val2 == (Object)null || (Object)(object)val2.QuickSlotMngr == (Object)null)
			{
				return;
			}
			for (int i = 0; i < 8; i++)
			{
				if (KeyDown(s_slotKeys[i]))
				{
					if (ShiftHeld)
					{
						ActivateExtra(val2, i);
					}
					else if (CtrlHeld)
					{
						MoveUpToExtra(val2, i);
					}
				}
			}
		}
		catch (Exception ex)
		{
			Log.LogError((object)("Update: " + ex));
		}
	}

	private static string DescribeDisplay(Component d)
	{
		//IL_001f: 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)
		string text = "?";
		Transform transform = d.transform;
		RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
		if ((Object)(object)val != (Object)null)
		{
			text = val.anchoredPosition.x.ToString("F1");
		}
		string text2 = "null";
		string text3 = "-";
		if (s_refQuickSlotField != null)
		{
			object? value = s_refQuickSlotField.GetValue(d);
			QuickSlot val2 = (QuickSlot)((value is QuickSlot) ? value : null);
			if ((Object)(object)val2 != (Object)null)
			{
				text2 = val2.Index.ToString();
				text3 = ((!((Object)(object)val2.RegisteredItem != (Object)null)) ? "(empty)" : val2.RegisteredItem.Name);
			}
		}
		return string.Concat("x=", text, " RefSlotID=", s_refSlotField.GetValue(d), " boundSlot=", text2, " item='", text3, "' activeSelf=", d.gameObject.activeSelf, " sibling=", d.transform.GetSiblingIndex(), " name='", ((Object)d.gameObject).name, "'");
	}

	private static void DumpQuickslotUi()
	{
		try
		{
			Character val = ((!((Object)(object)CharacterManager.Instance != (Object)null)) ? null : CharacterManager.Instance.GetFirstLocalCharacter());
			if ((Object)(object)val == (Object)null || (Object)(object)val.CharacterUI == (Object)null)
			{
				Log.LogMessage((object)"No character UI.");
				return;
			}
			Log.LogMessage((object)("=== Quickslot UI dump (v1.0.0) baseCount=" + s_baseCount + " ==="));
			Component[] componentsInChildren = ((Component)val.CharacterUI).GetComponentsInChildren(s_panelType, true);
			Log.LogMessage((object)(componentsInChildren.Length + " KeyboardQuickSlotPanel(s):"));
			Component[] array = componentsInChildren;
			foreach (Component val2 in array)
			{
				string text = ((Object)val2.transform).name;
				Transform parent = val2.transform.parent;
				while ((Object)(object)parent != (Object)null)
				{
					text = ((Object)parent).name + "/" + text;
					parent = parent.parent;
				}
				Component[] componentsInChildren2 = val2.GetComponentsInChildren(s_displayType, true);
				Log.LogMessage((object)("PANEL '" + text + "' activeInHierarchy=" + val2.gameObject.activeInHierarchy + " displaysInSubtree=" + ((componentsInChildren2 != null) ? componentsInChildren2.Length : 0)));
				if (componentsInChildren2 != null)
				{
					Component[] array2 = componentsInChildren2;
					foreach (Component d in array2)
					{
						Log.LogMessage((object)("    " + DescribeDisplay(d)));
					}
				}
			}
			if ((Object)(object)s_uiRow != (Object)null && s_uiDisplays != null)
			{
				Log.LogMessage((object)("EXTRA ROW '" + ((Object)s_uiRow).name + "' active=" + s_uiRow.activeInHierarchy));
				for (int k = 0; k < s_uiDisplays.Length; k++)
				{
					Component val3 = s_uiDisplays[k];
					Log.LogMessage((object)("    mine[" + k + "] key=" + s_slotLabels[k] + " -> " + ((!((Object)(object)val3 == (Object)null)) ? DescribeDisplay(val3) : "NULL")));
				}
			}
			else
			{
				Log.LogMessage((object)"EXTRA ROW: not built yet.");
			}
			CharacterQuickSlotManager quickSlotMngr = val.QuickSlotMngr;
			if ((Object)(object)quickSlotMngr != (Object)null && s_baseCount >= 0)
			{
				Log.LogMessage((object)"MANAGER extra slots:");
				for (int l = 0; l < 8; l++)
				{
					QuickSlot quickSlot = quickSlotMngr.GetQuickSlot(s_baseCount + l);
					Log.LogMessage((object)("    slot " + (s_baseCount + l) + " (key " + s_slotLabels[l] + "): " + ((!((Object)(object)quickSlot == (Object)null)) ? ((!((Object)(object)quickSlot.RegisteredItem != (Object)null)) ? "(empty)" : ("'" + quickSlot.RegisteredItem.Name + "'")) : "NULL")));
				}
			}
			Log.LogMessage((object)"=== end dump ===");
		}
		catch (Exception ex)
		{
			Log.LogError((object)("DumpQuickslotUi: " + ex));
		}
	}

	private void MaintainUiRow()
	{
		//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_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: Expected O, but got Unknown
		//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_028c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0291: Unknown result type (might be due to invalid IL or missing references)
		//IL_029d: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
		if (s_baseCount < 0 || s_panelType == null || s_displayType == null || s_refSlotField == null)
		{
			return;
		}
		Character val = ((!((Object)(object)CharacterManager.Instance != (Object)null)) ? null : CharacterManager.Instance.GetFirstLocalCharacter());
		if ((Object)(object)val == (Object)null || (Object)(object)val.CharacterUI == (Object)null || (Object)(object)val.QuickSlotMngr == (Object)null || ((Object)(object)s_uiRow != (Object)null && s_uiRowOwner == ((object)val.UID/*cast due to .constrained prefix*/).ToString()))
		{
			return;
		}
		Component val2 = null;
		Component[] componentsInChildren = ((Component)val.CharacterUI).GetComponentsInChildren(s_panelType, true);
		foreach (Component val3 in componentsInChildren)
		{
			if (val3.gameObject.activeInHierarchy)
			{
				Component[] componentsInChildren2 = val3.GetComponentsInChildren(s_displayType, false);
				if (componentsInChildren2 != null && componentsInChildren2.Length == 8)
				{
					val2 = val3;
					break;
				}
			}
		}
		if ((Object)(object)val2 == (Object)null)
		{
			return;
		}
		try
		{
			GameObject val4 = new GameObject("ExtraQuickslotHolder");
			val4.SetActive(false);
			val4.transform.SetParent(val2.transform.parent, false);
			GameObject val5 = Object.Instantiate<GameObject>(val2.gameObject, val4.transform, false);
			((Object)val5).name = "ExtraQuickslotRow";
			Component[] componentsInChildren3 = val5.GetComponentsInChildren(s_panelType, true);
			foreach (Component val6 in componentsInChildren3)
			{
				Object.DestroyImmediate((Object)(object)val6);
			}
			Component[] componentsInChildren4 = val5.GetComponentsInChildren(s_displayType, true);
			Component[] array = (Component[])(object)new Component[8];
			Component[] array2 = componentsInChildren4;
			foreach (Component val7 in array2)
			{
				int num = (int)s_refSlotField.GetValue(val7);
				if (num >= 0 && num < 8)
				{
					int num2 = s_vanillaSlotToLabel[num];
					s_refSlotField.SetValue(val7, s_baseCount + num2);
					array[num2] = val7;
				}
			}
			val5.transform.SetParent(val2.transform.parent, false);
			RectTransform component = val5.GetComponent<RectTransform>();
			RectTransform component2 = val2.GetComponent<RectTransform>();
			if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null)
			{
				Vector2 anchoredPosition = component2.anchoredPosition;
				float y = anchoredPosition.y;
				Rect rect = component2.rect;
				anchoredPosition.y = y + ((Rect)(ref rect)).height * 1.1f;
				component.anchoredPosition = anchoredPosition;
			}
			Object.Destroy((Object)(object)val4);
			val5.SetActive(true);
			s_uiRow = val5;
			s_uiRowOwner = ((object)val.UID/*cast due to .constrained prefix*/).ToString();
			s_uiDisplays = array;
			EnforceBindings(val);
			MethodInfo method = s_displayType.GetMethod("SetInputTargetAlpha");
			FieldInfo field = s_displayType.GetField("m_inputCanvasGroup", BindingFlags.Instance | BindingFlags.NonPublic);
			Component[] array3 = array;
			foreach (Component val8 in array3)
			{
				if ((Object)(object)val8 == (Object)null)
				{
					continue;
				}
				try
				{
					if (method != null)
					{
						method.Invoke(val8, new object[1] { 0f });
					}
					if (!(field != null))
					{
						continue;
					}
					object? value = field.GetValue(val8);
					Component val9 = (Component)((value is Component) ? value : null);
					if ((Object)(object)val9 != (Object)null)
					{
						PropertyInfo property = ((object)val9).GetType().GetProperty("alpha");
						if (property != null)
						{
							property.SetValue(val9, 0f, null);
						}
					}
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("Input hint hide failed: " + ex.Message));
				}
			}
			Log.LogInfo((object)("Extra quickslot row built: " + componentsInChildren4.Length + " displays."));
		}
		catch (Exception ex2)
		{
			Log.LogError((object)("MaintainUiRow: " + ex2));
		}
	}

	private static void EnforceBindings(Character character)
	{
		if (s_uiDisplays == null || s_setQuickSlot == null || (Object)(object)character.QuickSlotMngr == (Object)null)
		{
			return;
		}
		for (int i = 0; i < s_uiDisplays.Length && i < 8; i++)
		{
			QuickSlot quickSlot = character.QuickSlotMngr.GetQuickSlot(s_baseCount + i);
			if (!((Object)(object)s_uiDisplays[i] == (Object)null) && !((Object)(object)quickSlot == (Object)null) && (!(s_refQuickSlotField != null) || !object.ReferenceEquals(s_refQuickSlotField.GetValue(s_uiDisplays[i]), quickSlot)))
			{
				try
				{
					s_setQuickSlot.Invoke(s_uiDisplays[i], new object[1] { quickSlot });
				}
				catch
				{
				}
			}
		}
	}

	private static void ActivateExtra(Character character, int i)
	{
		CharacterQuickSlotManager quickSlotMngr = character.QuickSlotMngr;
		if (s_baseCount < 0)
		{
			Log.LogWarning((object)"Extra slots not initialized yet.");
			return;
		}
		int num = s_baseCount + i;
		QuickSlot quickSlot = quickSlotMngr.GetQuickSlot(num);
		if ((Object)(object)quickSlot == (Object)null || quickSlot.IsFree)
		{
			Log.LogMessage((object)("Extra slot " + s_slotLabels[i] + " is empty (Ctrl+" + s_slotLabels[i] + " to assign from the vanilla bar)."));
		}
		else
		{
			quickSlotMngr.QuickSlotInput(num);
		}
	}

	private static void MoveUpToExtra(Character character, int i)
	{
		CharacterQuickSlotManager quickSlotMngr = character.QuickSlotMngr;
		if (s_baseCount < 0)
		{
			return;
		}
		QuickSlot quickSlot = quickSlotMngr.GetQuickSlot(s_labelToVanillaSlot[i]);
		QuickSlot quickSlot2 = quickSlotMngr.GetQuickSlot(s_baseCount + i);
		if (!((Object)(object)quickSlot == (Object)null) && !((Object)(object)quickSlot2 == (Object)null))
		{
			Item registeredItem = quickSlot.RegisteredItem;
			if ((Object)(object)registeredItem == (Object)null)
			{
				Log.LogMessage((object)("Vanilla slot " + s_slotLabels[i] + " is empty; nothing to move up."));
				return;
			}
			quickSlot.Clear();
			quickSlot2.SetQuickSlot(registeredItem, true);
			Log.LogMessage((object)("Moved '" + registeredItem.Name + "' to extra slot " + s_slotLabels[i] + " (activate with Shift+" + s_slotLabels[i] + ")."));
		}
	}

	static ExtraQuickslotsPlugin()
	{
		KeyCode[] array = new KeyCode[8];
		RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
		s_slotKeys = (KeyCode[])(object)array;
		s_slotLabels = new string[8] { "Q", "E", "R", "1", "2", "3", "4", "5" };
		s_labelToVanillaSlot = new int[8] { 5, 6, 7, 0, 1, 2, 3, 4 };
		s_vanillaSlotToLabel = new int[8] { 3, 4, 5, 6, 7, 0, 1, 2 };
		s_inputType = Type.GetType("UnityEngine.Input, UnityEngine.InputLegacyModule");
		s_getKey = ((!(s_inputType != null)) ? null : s_inputType.GetMethod("GetKey", new Type[1] { typeof(KeyCode) }));
		s_getKeyDown = ((!(s_inputType != null)) ? null : s_inputType.GetMethod("GetKeyDown", new Type[1] { typeof(KeyCode) }));
		s_panelType = typeof(CharacterQuickSlotManager).Assembly.GetType("KeyboardQuickSlotPanel");
		s_displayType = typeof(CharacterQuickSlotManager).Assembly.GetType("QuickSlotDisplay");
		s_refSlotField = ((!(s_displayType != null)) ? null : s_displayType.GetField("RefSlotID"));
		s_refQuickSlotField = ((!(s_displayType != null)) ? null : s_displayType.GetField("m_refQuickSlot", BindingFlags.Instance | BindingFlags.NonPublic));
		s_setQuickSlot = ((!(s_displayType != null)) ? null : s_displayType.GetMethod("SetQuickSlot"));
	}
}
[HarmonyPatch(typeof(CharacterQuickSlotManager), "Awake")]
internal static class QuickSlotManager_Awake
{
	[HarmonyPostfix]
	private static void Postfix(CharacterQuickSlotManager __instance)
	{
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Expected O, but got Unknown
		try
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(CharacterQuickSlotManager), "m_quickSlots");
			FieldInfo fieldInfo2 = AccessTools.Field(typeof(CharacterQuickSlotManager), "m_character");
			if (fieldInfo == null)
			{
				ExtraQuickslotsPlugin.Log.LogError((object)"m_quickSlots not found.");
				return;
			}
			if (!(fieldInfo.GetValue(__instance) is QuickSlot[] array))
			{
				ExtraQuickslotsPlugin.Log.LogWarning((object)"Quickslot array null at Awake.");
				return;
			}
			int num = array.Length;
			if (ExtraQuickslotsPlugin.s_baseCount < 0)
			{
				ExtraQuickslotsPlugin.s_baseCount = num;
			}
			Character val = (Character)((!(fieldInfo2 != null)) ? null : /*isinst with value type is only supported in some contexts*/);
			GameObject val2 = new GameObject("ExtraQuickslots");
			val2.transform.SetParent(((Component)__instance).transform, false);
			QuickSlot[] array2 = (QuickSlot[])(object)new QuickSlot[num + 8];
			Array.Copy(array, array2, num);
			for (int i = 0; i < 8; i++)
			{
				QuickSlot val3 = val2.AddComponent<QuickSlot>();
				val3.Index = num + i;
				if ((Object)(object)val != (Object)null)
				{
					val3.SetOwner(val);
				}
				array2[num + i] = val3;
			}
			fieldInfo.SetValue(__instance, array2);
			ExtraQuickslotsPlugin.Log.LogInfo((object)("Extra quickslots attached: indices " + num + ".." + (num + 8 - 1) + "."));
		}
		catch (Exception ex)
		{
			ExtraQuickslotsPlugin.Log.LogError((object)("Awake patch: " + ex));
		}
	}
}
[HarmonyPatch]
internal static class SuppressVanillaWhileShift
{
	private static IEnumerable<MethodBase> TargetMethods()
	{
		for (int n = 1; n <= 8; n++)
		{
			MethodInfo m = AccessTools.Method(typeof(ControlsInput), "QuickSlotInstant" + n, (Type[])null, (Type[])null);
			if (m != null)
			{
				yield return m;
			}
		}
	}

	[HarmonyPostfix]
	private static void Postfix(ref bool __result)
	{
		if (__result && (ExtraQuickslotsPlugin.ShiftHeld || ExtraQuickslotsPlugin.CtrlHeldPublic))
		{
			__result = false;
		}
	}
}