Decompiled source of InstantItemSlots v4.0.9

BepInEx/plugins/InstantItemSlots/InstantItemSlots.dll

Decompiled 3 days ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Utilities;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("CustomKeybinds")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CustomKeybinds")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("32460eb4-e201-4ed4-9723-296aac0d0d64")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace InstantItemSlots;

[BepInPlugin("kviks.instantitemslots", "Instant Item Slots", "4.0.2")]
public class InstantItemSlotsPlugin : BaseUnityPlugin
{
	internal static ManualLogSource Log;

	internal static bool DebugEnabled;

	internal static bool FixEmotesEnabled;

	internal static int ControlledSlots;

	internal static float CooldownSeconds;

	internal static float SpawnDelaySeconds;

	internal static bool IgnoreBusyDuringSpawnDelay;

	internal static bool IgnoreBusyChecksAlways;

	internal static bool EnsureArrowBindings;

	internal static bool PreventDuplicateArrowBindings;

	internal static bool PlayGrabSfx;

	internal static bool SyncSteps;

	private static Harmony _harmony;

	private static ConfigEntry<bool> _debug;

	private static ConfigEntry<bool> _fixEmotes;

	private static ConfigEntry<int> _controlledSlots;

	private static ConfigEntry<float> _cooldownSeconds;

	private static ConfigEntry<float> _spawnDelaySeconds;

	private static ConfigEntry<bool> _ignoreBusyDuringSpawnDelay;

	private static ConfigEntry<bool> _ignoreBusyChecksAlways;

	private static ConfigEntry<bool> _ensureArrowBindings;

	private static ConfigEntry<bool> _preventDuplicateArrowBindings;

	private static ConfigEntry<bool> _playGrabSfx;

	private static ConfigEntry<bool> _syncSteps;

	private void Awake()
	{
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		_debug = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Enables debug logging.");
		_fixEmotes = ((BaseUnityPlugin)this).Config.Bind<bool>("Emotes", "FixEmotesToArrowsOnly", true, "Forces emotes to only trigger from Up/Down arrows and adds those bindings.");
		_controlledSlots = ((BaseUnityPlugin)this).Config.Bind<int>("Hotbar", "ControlledSlots", 4, "How many slots are controlled by 1/2/3/4 (1..4).");
		_cooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Hotbar", "CooldownSeconds", 0.3f, "Minimum time since last switch allowed.");
		_spawnDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Spawn", "SpawnDelaySeconds", 0f, "Seconds after level load where switching rules can be relaxed.");
		_ignoreBusyDuringSpawnDelay = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawn", "IgnoreBusyChecksDuringSpawnDelay", true, "Ignore animation/interact/activatingItem during SpawnDelaySeconds.");
		_ignoreBusyChecksAlways = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawn", "IgnoreBusyChecksAlways", false, "Always ignore animation/interact/activatingItem checks.");
		_ensureArrowBindings = ((BaseUnityPlugin)this).Config.Bind<bool>("Emotes", "EnsureArrowBindings", true, "Ensures Up/Down are bound to Emote1/Emote2.");
		_preventDuplicateArrowBindings = ((BaseUnityPlugin)this).Config.Bind<bool>("Emotes", "PreventDuplicateArrowBindings", true, "Prevents adding the same binding multiple times.");
		_playGrabSfx = ((BaseUnityPlugin)this).Config.Bind<bool>("Hotbar", "PlayGrabSfx", true, "Play grab sound when switching.");
		_syncSteps = ((BaseUnityPlugin)this).Config.Bind<bool>("Hotbar", "SyncStepsServerRpc", true, "Call SwitchItemSlotsServerRpc steps to sync with others.");
		ApplyConfig();
		HookConfigChanges();
		GameAccess.Init();
		_harmony = new Harmony("kviks.instantitemslots");
		_harmony.PatchAll();
		if (DebugEnabled)
		{
			Log.LogInfo((object)"[IIS] Loaded.");
		}
	}

	private void ApplyConfig()
	{
		DebugEnabled = _debug.Value;
		FixEmotesEnabled = _fixEmotes.Value;
		ControlledSlots = Mathf.Clamp(_controlledSlots.Value, 1, 4);
		CooldownSeconds = Mathf.Max(0f, _cooldownSeconds.Value);
		SpawnDelaySeconds = Mathf.Max(0f, _spawnDelaySeconds.Value);
		IgnoreBusyDuringSpawnDelay = _ignoreBusyDuringSpawnDelay.Value;
		IgnoreBusyChecksAlways = _ignoreBusyChecksAlways.Value;
		EnsureArrowBindings = _ensureArrowBindings.Value;
		PreventDuplicateArrowBindings = _preventDuplicateArrowBindings.Value;
		PlayGrabSfx = _playGrabSfx.Value;
		SyncSteps = _syncSteps.Value;
	}

	private void HookConfigChanges()
	{
		_debug.SettingChanged += delegate
		{
			ApplyConfig();
		};
		_fixEmotes.SettingChanged += delegate
		{
			ApplyConfig();
		};
		_controlledSlots.SettingChanged += delegate
		{
			ApplyConfig();
		};
		_cooldownSeconds.SettingChanged += delegate
		{
			ApplyConfig();
		};
		_spawnDelaySeconds.SettingChanged += delegate
		{
			ApplyConfig();
		};
		_ignoreBusyDuringSpawnDelay.SettingChanged += delegate
		{
			ApplyConfig();
		};
		_ignoreBusyChecksAlways.SettingChanged += delegate
		{
			ApplyConfig();
		};
		_ensureArrowBindings.SettingChanged += delegate
		{
			ApplyConfig();
		};
		_preventDuplicateArrowBindings.SettingChanged += delegate
		{
			ApplyConfig();
		};
		_playGrabSfx.SettingChanged += delegate
		{
			ApplyConfig();
		};
		_syncSteps.SettingChanged += delegate
		{
			ApplyConfig();
		};
	}

	internal static void HandleLocalUpdate(PlayerControllerB player)
	{
		if ((Object)(object)player == (Object)null || !((NetworkBehaviour)player).IsOwner || (!player.isPlayerControlled && !player.isTestingPlayer))
		{
			return;
		}
		Keyboard current = Keyboard.current;
		if (current == null)
		{
			return;
		}
		int num = -1;
		if (current.digit1Key != null && ((ButtonControl)current.digit1Key).wasPressedThisFrame)
		{
			num = 0;
		}
		else if (current.digit2Key != null && ((ButtonControl)current.digit2Key).wasPressedThisFrame)
		{
			num = 1;
		}
		else if (current.digit3Key != null && ((ButtonControl)current.digit3Key).wasPressedThisFrame)
		{
			num = 2;
		}
		else if (current.digit4Key != null && ((ButtonControl)current.digit4Key).wasPressedThisFrame)
		{
			num = 3;
		}
		if (num >= 0)
		{
			if (DebugEnabled)
			{
				Log.LogInfo((object)("[IIS] Key pressed -> slot " + (num + 1)));
			}
			TrySwitchToSlot(player, num);
		}
	}

	private static void TrySwitchToSlot(PlayerControllerB player, int targetSlot)
	{
		try
		{
			if (!GameAccess.Ready)
			{
				if (DebugEnabled)
				{
					Log.LogWarning((object)"[IIS] Not ready: reflection cache missing.");
				}
				return;
			}
			if (player.inTerminalMenu || player.isTypingChat || player.inSpecialMenu)
			{
				if (DebugEnabled)
				{
					Log.LogInfo((object)"[IIS] Blocked: menu/terminal/chat.");
				}
				return;
			}
			bool flag = SpawnDelaySeconds > 0f && Time.timeSinceLevelLoad < SpawnDelaySeconds;
			if (!IgnoreBusyChecksAlways && (!flag || !IgnoreBusyDuringSpawnDelay) && (player.isGrabbingObjectAnimation || player.inSpecialInteractAnimation || player.activatingItem))
			{
				if (DebugEnabled)
				{
					Log.LogInfo((object)"[IIS] Blocked: animation/interact/activatingItem.");
				}
				return;
			}
			if (player.jetpackControls || player.disablingJetpackControls)
			{
				if (DebugEnabled)
				{
					Log.LogInfo((object)"[IIS] Blocked: jetpackControls.");
				}
				return;
			}
			if ((Object)(object)player.quickMenuManager != (Object)null && player.quickMenuManager.isMenuOpen)
			{
				if (DebugEnabled)
				{
					Log.LogInfo((object)"[IIS] Blocked: quick menu.");
				}
				return;
			}
			if (GameAccess.GetThrowingObject(player))
			{
				if (DebugEnabled)
				{
					Log.LogInfo((object)"[IIS] Blocked: throwingObject.");
				}
				return;
			}
			float timeSinceSwitchingSlots = GameAccess.GetTimeSinceSwitchingSlots(player);
			if (timeSinceSwitchingSlots >= 0f && timeSinceSwitchingSlots < CooldownSeconds)
			{
				if (DebugEnabled)
				{
					Log.LogInfo((object)("[IIS] Blocked: cooldown timeSinceSwitchingSlots=" + timeSinceSwitchingSlots));
				}
				return;
			}
			GrabbableObject[] itemSlots = player.ItemSlots;
			if (itemSlots == null || itemSlots.Length == 0)
			{
				return;
			}
			int num = Mathf.Min(itemSlots.Length, ControlledSlots);
			if (num <= 0)
			{
				return;
			}
			targetSlot = Mathf.Clamp(targetSlot, 0, num - 1);
			int currentItemSlot = player.currentItemSlot;
			if (currentItemSlot == targetSlot)
			{
				return;
			}
			int num2 = (targetSlot - currentItemSlot + num) % num;
			int num3 = (currentItemSlot - targetSlot + num) % num;
			bool flag2 = num2 <= num3;
			int num4 = (flag2 ? num2 : num3);
			if (num4 > 0)
			{
				ShipBuildModeManager instance = ShipBuildModeManager.Instance;
				if ((Object)(object)instance != (Object)null && instance.InBuildMode)
				{
					instance.CancelBuildMode(true);
				}
				for (int i = 0; i < num4; i++)
				{
					DoOneStep(player, flag2, num);
				}
				GameAccess.SetTimeSinceSwitchingSlots(player, 0f);
			}
		}
		catch (Exception ex)
		{
			Log.LogError((object)("[IIS] Switch error: " + ex));
		}
	}

	private static void DoOneStep(PlayerControllerB player, bool forward, int len)
	{
		int slot = (forward ? ((player.currentItemSlot + 1) % len) : ((player.currentItemSlot == 0) ? (len - 1) : (player.currentItemSlot - 1)));
		GameAccess.SwitchToItemSlot(player, slot);
		if (SyncSteps)
		{
			GameAccess.SwitchItemSlotsServerRpc(player, forward);
		}
		if (!PlayGrabSfx || !((Object)(object)player.currentlyHeldObjectServer != (Object)null))
		{
			return;
		}
		Item itemProperties = player.currentlyHeldObjectServer.itemProperties;
		if ((Object)(object)itemProperties != (Object)null && (Object)(object)itemProperties.grabSFX != (Object)null)
		{
			AudioSource component = ((Component)player.currentlyHeldObjectServer).gameObject.GetComponent<AudioSource>();
			if ((Object)(object)component != (Object)null)
			{
				component.PlayOneShot(itemProperties.grabSFX, 0.6f);
			}
		}
	}
}
internal static class GameAccess
{
	internal static bool Ready;

	private static MethodInfo _switchToItemSlot;

	private static MethodInfo _switchItemSlotsServerRpc;

	private static FieldInfo _throwingObject;

	private static FieldInfo _timeSinceSwitchingSlots;

	internal static void Init()
	{
		try
		{
			Type typeFromHandle = typeof(PlayerControllerB);
			_switchToItemSlot = AccessTools.Method(typeFromHandle, "SwitchToItemSlot", new Type[2]
			{
				typeof(int),
				typeof(GrabbableObject)
			}, (Type[])null);
			_switchItemSlotsServerRpc = AccessTools.Method(typeFromHandle, "SwitchItemSlotsServerRpc", new Type[1] { typeof(bool) }, (Type[])null);
			_throwingObject = AccessTools.Field(typeFromHandle, "throwingObject");
			_timeSinceSwitchingSlots = AccessTools.Field(typeFromHandle, "timeSinceSwitchingSlots");
			Ready = _switchToItemSlot != null && _switchItemSlotsServerRpc != null && _throwingObject != null && _timeSinceSwitchingSlots != null;
			if (InstantItemSlotsPlugin.DebugEnabled && InstantItemSlotsPlugin.Log != null)
			{
				InstantItemSlotsPlugin.Log.LogInfo((object)("[IIS] Reflection: SwitchToItemSlot=" + (_switchToItemSlot != null) + " SwitchItemSlotsServerRpc=" + (_switchItemSlotsServerRpc != null) + " throwingObject=" + (_throwingObject != null) + " timeSinceSwitchingSlots=" + (_timeSinceSwitchingSlots != null)));
			}
		}
		catch (Exception ex)
		{
			Ready = false;
			if (InstantItemSlotsPlugin.Log != null)
			{
				InstantItemSlotsPlugin.Log.LogError((object)("[IIS] Reflection init failed: " + ex));
			}
		}
	}

	internal static void SwitchToItemSlot(PlayerControllerB player, int slot)
	{
		_switchToItemSlot.Invoke(player, new object[2] { slot, null });
	}

	internal static void SwitchItemSlotsServerRpc(PlayerControllerB player, bool forward)
	{
		_switchItemSlotsServerRpc.Invoke(player, new object[1] { forward });
	}

	internal static bool GetThrowingObject(PlayerControllerB player)
	{
		try
		{
			return (bool)_throwingObject.GetValue(player);
		}
		catch
		{
			return false;
		}
	}

	internal static float GetTimeSinceSwitchingSlots(PlayerControllerB player)
	{
		try
		{
			return (float)_timeSinceSwitchingSlots.GetValue(player);
		}
		catch
		{
			return -1f;
		}
	}

	internal static void SetTimeSinceSwitchingSlots(PlayerControllerB player, float value)
	{
		try
		{
			_timeSinceSwitchingSlots.SetValue(player, value);
		}
		catch
		{
		}
	}

	internal static void EnsureEmoteBindings()
	{
		if (!InstantItemSlotsPlugin.EnsureArrowBindings)
		{
			return;
		}
		try
		{
			IngamePlayerSettings instance = IngamePlayerSettings.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.playerInput == (Object)null)
			{
				return;
			}
			InputActionAsset actions = instance.playerInput.actions;
			if (!((Object)(object)actions == (Object)null))
			{
				InputAction val = actions.FindAction("Emote1", false);
				InputAction val2 = actions.FindAction("Emote2", false);
				if (val != null)
				{
					AddBindingOnce(val, "<Keyboard>/upArrow");
				}
				if (val2 != null)
				{
					AddBindingOnce(val2, "<Keyboard>/downArrow");
				}
			}
		}
		catch
		{
		}
	}

	private static void AddBindingOnce(InputAction action, string path)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		if (!InstantItemSlotsPlugin.PreventDuplicateArrowBindings)
		{
			InputActionSetupExtensions.AddBinding(action, path, (string)null, (string)null, (string)null);
			return;
		}
		ReadOnlyArray<InputBinding> bindings = action.bindings;
		for (int i = 0; i < bindings.Count; i++)
		{
			InputBinding val = bindings[i];
			if (string.Equals(((InputBinding)(ref val)).path, path, StringComparison.OrdinalIgnoreCase))
			{
				return;
			}
		}
		InputActionSetupExtensions.AddBinding(action, path, (string)null, (string)null, (string)null);
	}
}
[HarmonyPatch]
internal static class Patches
{
	[HarmonyPatch(typeof(PlayerControllerB), "Update")]
	[HarmonyPostfix]
	private static void PlayerUpdatePostfix(PlayerControllerB __instance)
	{
		InstantItemSlotsPlugin.HandleLocalUpdate(__instance);
	}

	[HarmonyPatch(typeof(PlayerControllerB), "OnEnable")]
	[HarmonyPostfix]
	private static void PlayerOnEnablePostfix(PlayerControllerB __instance)
	{
		if (InstantItemSlotsPlugin.FixEmotesEnabled)
		{
			GameAccess.EnsureEmoteBindings();
			if (InstantItemSlotsPlugin.DebugEnabled && InstantItemSlotsPlugin.Log != null)
			{
				InstantItemSlotsPlugin.Log.LogInfo((object)"[IIS] Emote bindings ensured: UpArrow/DownArrow.");
			}
		}
	}

	[HarmonyPatch(typeof(PlayerControllerB), "PerformEmote")]
	[HarmonyPrefix]
	private static bool PerformEmotePrefix(CallbackContext context, int emoteID)
	{
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Invalid comparison between Unknown and I4
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Invalid comparison between Unknown and I4
		if (!InstantItemSlotsPlugin.FixEmotesEnabled)
		{
			return true;
		}
		try
		{
			if (!((CallbackContext)(ref context)).performed)
			{
				return false;
			}
			InputControl control = ((CallbackContext)(ref context)).control;
			KeyControl val = (KeyControl)(object)((control is KeyControl) ? control : null);
			if (val == null)
			{
				return false;
			}
			Key keyCode = val.keyCode;
			return (int)keyCode == 63 || (int)keyCode == 64;
		}
		catch (Exception ex)
		{
			if (InstantItemSlotsPlugin.Log != null)
			{
				InstantItemSlotsPlugin.Log.LogError((object)("[IIS] PerformEmote filter error: " + ex));
			}
			return true;
		}
	}
}