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;
}
}
}