using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using GameNetcodeStuff;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
[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")]
[BepInPlugin("kviks.instantitemslots", "Instant Item Slots", "1.4.0")]
public class InstantItemSlots : BaseUnityPlugin
{
private static readonly BindingFlags BF = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private ConfigEntry<bool> EnableMod;
private ConfigEntry<bool> DebugLogs;
private ConfigEntry<bool> BlockEmoteOnDigit1And2;
private ConfigEntry<Key> DanceKey;
private ConfigEntry<Key> PointKey;
private ConfigEntry<int> DanceEmoteId;
private ConfigEntry<int> PointEmoteId;
private ConfigEntry<bool> InstantSlotSwitch;
private ConfigEntry<float> SlotSwitchCooldownSeconds;
private ConfigEntry<bool> AllowNumpadForSlots;
private ConfigEntry<Key> Slot1Key;
private ConfigEntry<Key> Slot2Key;
private ConfigEntry<Key> Slot3Key;
private ConfigEntry<Key> Slot4Key;
private ConfigEntry<Key> Slot1AltKey;
private ConfigEntry<Key> Slot2AltKey;
private ConfigEntry<Key> Slot3AltKey;
private ConfigEntry<Key> Slot4AltKey;
private static MethodInfo _miSwitchToItemSlot;
private static MethodInfo _miSwitchToItemSlotServerRpcInt;
private static MethodInfo _miSwitchItemSlotsServerRpcBool;
private static MethodInfo _miNextItemSlotBool;
private static MethodInfo _miCheckConditionsForEmote;
private static FieldInfo _fiTimeSinceStartingEmote;
private static FieldInfo _fiTimeSinceSwitchingSlots;
private static FieldInfo _fiInTerminalMenu;
private static FieldInfo _fiIsGrabbingObjectAnimation;
private static FieldInfo _fiInSpecialInteractAnimation;
private static FieldInfo _fiThrowingObject;
private static FieldInfo _fiActivatingItem;
private static FieldInfo _fiJetpackControls;
private static FieldInfo _fiDisablingJetpackControls;
private static FieldInfo _fiIsTestingPlayer;
private bool _emoteBindingsPatchedOnce;
private float _lastPatchTryTime;
private void Awake()
{
EnableMod = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableMod", true, (ConfigDescription)null);
DebugLogs = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogs", false, (ConfigDescription)null);
BlockEmoteOnDigit1And2 = ((BaseUnityPlugin)this).Config.Bind<bool>("Emotes", "BlockEmoteOnDigit1And2", true, (ConfigDescription)null);
DanceKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Emotes", "DanceKey", (Key)63, (ConfigDescription)null);
PointKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Emotes", "PointKey", (Key)64, (ConfigDescription)null);
DanceEmoteId = ((BaseUnityPlugin)this).Config.Bind<int>("Emotes", "DanceEmoteId", 1, (ConfigDescription)null);
PointEmoteId = ((BaseUnityPlugin)this).Config.Bind<int>("Emotes", "PointEmoteId", 2, (ConfigDescription)null);
InstantSlotSwitch = ((BaseUnityPlugin)this).Config.Bind<bool>("Slots", "InstantSlotSwitch", true, (ConfigDescription)null);
SlotSwitchCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Slots", "SlotSwitchCooldownSeconds", 0f, (ConfigDescription)null);
AllowNumpadForSlots = ((BaseUnityPlugin)this).Config.Bind<bool>("Slots", "AllowNumpadForSlots", true, (ConfigDescription)null);
Slot1Key = ((BaseUnityPlugin)this).Config.Bind<Key>("Slots", "Slot1Key", (Key)41, (ConfigDescription)null);
Slot2Key = ((BaseUnityPlugin)this).Config.Bind<Key>("Slots", "Slot2Key", (Key)42, (ConfigDescription)null);
Slot3Key = ((BaseUnityPlugin)this).Config.Bind<Key>("Slots", "Slot3Key", (Key)43, (ConfigDescription)null);
Slot4Key = ((BaseUnityPlugin)this).Config.Bind<Key>("Slots", "Slot4Key", (Key)44, (ConfigDescription)null);
Slot1AltKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Slots", "Slot1AltKey", (Key)85, (ConfigDescription)null);
Slot2AltKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Slots", "Slot2AltKey", (Key)86, (ConfigDescription)null);
Slot3AltKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Slots", "Slot3AltKey", (Key)87, (ConfigDescription)null);
Slot4AltKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Slots", "Slot4AltKey", (Key)88, (ConfigDescription)null);
CacheReflection();
}
private void Update()
{
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
//IL_012c: Unknown result type (might be due to invalid IL or missing references)
//IL_014b: Unknown result type (might be due to invalid IL or missing references)
//IL_0170: Unknown result type (might be due to invalid IL or missing references)
//IL_018f: Unknown result type (might be due to invalid IL or missing references)
//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
//IL_0217: Unknown result type (might be due to invalid IL or missing references)
if (!EnableMod.Value)
{
return;
}
Keyboard current = Keyboard.current;
if (current == null)
{
return;
}
PlayerControllerB localPlayer = GetLocalPlayer();
if ((Object)(object)localPlayer == (Object)null)
{
return;
}
if (Time.unscaledTime - _lastPatchTryTime > 1f)
{
_lastPatchTryTime = Time.unscaledTime;
if (BlockEmoteOnDigit1And2.Value && !_emoteBindingsPatchedOnce)
{
_emoteBindingsPatchedOnce = TryPatchEmoteDigitBindings();
}
}
if (InstantSlotSwitch.Value)
{
ForceSlotCooldown(localPlayer, SlotSwitchCooldownSeconds.Value);
}
if (!IsInputBlocked(localPlayer))
{
if (Pressed(DanceKey.Value))
{
TryEmote(localPlayer, ClampEmoteId(DanceEmoteId.Value));
}
if (Pressed(PointKey.Value))
{
TryEmote(localPlayer, ClampEmoteId(PointEmoteId.Value));
}
if (Pressed(Slot1Key.Value) || (AllowNumpadForSlots.Value && Pressed(Slot1AltKey.Value)))
{
SwitchSlotExact(localPlayer, 0);
}
if (Pressed(Slot2Key.Value) || (AllowNumpadForSlots.Value && Pressed(Slot2AltKey.Value)))
{
SwitchSlotExact(localPlayer, 1);
}
if (Pressed(Slot3Key.Value) || (AllowNumpadForSlots.Value && Pressed(Slot3AltKey.Value)))
{
SwitchSlotExact(localPlayer, 2);
}
if (Pressed(Slot4Key.Value) || (AllowNumpadForSlots.Value && Pressed(Slot4AltKey.Value)))
{
SwitchSlotExact(localPlayer, 3);
}
}
}
private static int ClampEmoteId(int id)
{
if (id < 1)
{
return 1;
}
if (id > 99)
{
return 99;
}
return id;
}
private static bool Pressed(Key key)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0003: Invalid comparison between Unknown and I4
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
if ((int)key == 0)
{
return false;
}
Keyboard current = Keyboard.current;
if (current == null)
{
return false;
}
KeyControl val;
try
{
val = current[key];
}
catch
{
return false;
}
return val != null && ((ButtonControl)val).wasPressedThisFrame;
}
private static PlayerControllerB GetLocalPlayer()
{
if ((Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)GameNetworkManager.Instance.localPlayerController != (Object)null)
{
return GameNetworkManager.Instance.localPlayerController;
}
if ((Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.localPlayerController != (Object)null)
{
return StartOfRound.Instance.localPlayerController;
}
return null;
}
private bool IsInputBlocked(PlayerControllerB p)
{
if ((Object)(object)p == (Object)null)
{
return true;
}
if (p.isTypingChat)
{
return true;
}
if (ReadBool(p, _fiInTerminalMenu))
{
return true;
}
if (ReadBool(p, _fiIsGrabbingObjectAnimation))
{
return true;
}
if (ReadBool(p, _fiInSpecialInteractAnimation))
{
return true;
}
if (ReadBool(p, _fiThrowingObject))
{
return true;
}
if (ReadBool(p, _fiActivatingItem))
{
return true;
}
if ((Object)(object)p.quickMenuManager != (Object)null && p.quickMenuManager.isMenuOpen)
{
return true;
}
if (ReadBool(p, _fiJetpackControls))
{
return true;
}
if (ReadBool(p, _fiDisablingJetpackControls))
{
return true;
}
return false;
}
private static bool ReadBool(PlayerControllerB p, FieldInfo fi)
{
if ((Object)(object)p == (Object)null || fi == null)
{
return false;
}
if (fi.FieldType != typeof(bool))
{
return false;
}
return (bool)fi.GetValue(p);
}
private static float ReadFloat(PlayerControllerB p, FieldInfo fi, float fallback)
{
if ((Object)(object)p == (Object)null || fi == null)
{
return fallback;
}
if (fi.FieldType != typeof(float))
{
return fallback;
}
return (float)fi.GetValue(p);
}
private static void WriteFloat(PlayerControllerB p, FieldInfo fi, float value)
{
if (!((Object)(object)p == (Object)null) && !(fi == null) && !(fi.FieldType != typeof(float)))
{
fi.SetValue(p, value);
}
}
private void CacheReflection()
{
Type typeFromHandle = typeof(PlayerControllerB);
_miSwitchToItemSlot = (from m in typeFromHandle.GetMethods(BF)
where m.Name == "SwitchToItemSlot"
orderby m.GetParameters().Length descending
select m).FirstOrDefault(delegate(MethodInfo m)
{
ParameterInfo[] parameters2 = m.GetParameters();
return parameters2.Length >= 1 && parameters2[0].ParameterType == typeof(int);
});
_miSwitchToItemSlotServerRpcInt = typeFromHandle.GetMethods(BF).FirstOrDefault(delegate(MethodInfo m)
{
if (!m.Name.Contains("ServerRpc"))
{
return false;
}
if (!m.Name.Contains("Switch") && !m.Name.Contains("ItemSlot"))
{
return false;
}
ParameterInfo[] parameters = m.GetParameters();
return parameters.Length == 1 && parameters[0].ParameterType == typeof(int);
});
_miSwitchItemSlotsServerRpcBool = typeFromHandle.GetMethods(BF).FirstOrDefault((MethodInfo m) => m.Name == "SwitchItemSlotsServerRpc" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(bool));
_miNextItemSlotBool = typeFromHandle.GetMethods(BF).FirstOrDefault((MethodInfo m) => m.Name == "NextItemSlot" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(bool) && m.ReturnType == typeof(int));
_miCheckConditionsForEmote = typeFromHandle.GetMethod("CheckConditionsForEmote", BF);
_fiTimeSinceStartingEmote = typeFromHandle.GetField("timeSinceStartingEmote", BF);
_fiTimeSinceSwitchingSlots = typeFromHandle.GetField("timeSinceSwitchingSlots", BF);
_fiInTerminalMenu = typeFromHandle.GetField("inTerminalMenu", BF);
_fiIsGrabbingObjectAnimation = typeFromHandle.GetField("isGrabbingObjectAnimation", BF);
_fiInSpecialInteractAnimation = typeFromHandle.GetField("inSpecialInteractAnimation", BF);
_fiThrowingObject = typeFromHandle.GetField("throwingObject", BF);
_fiActivatingItem = typeFromHandle.GetField("activatingItem", BF);
_fiJetpackControls = typeFromHandle.GetField("jetpackControls", BF);
_fiDisablingJetpackControls = typeFromHandle.GetField("disablingJetpackControls", BF);
_fiIsTestingPlayer = typeFromHandle.GetField("isTestingPlayer", BF);
}
private void ForceSlotCooldown(PlayerControllerB p, float seconds)
{
if (!(_fiTimeSinceSwitchingSlots == null))
{
float num = Mathf.Max(0f, seconds);
float num2 = ReadFloat(p, _fiTimeSinceSwitchingSlots, 999f);
if (num2 < num)
{
WriteFloat(p, _fiTimeSinceSwitchingSlots, num);
}
else if (num == 0f)
{
WriteFloat(p, _fiTimeSinceSwitchingSlots, 999f);
}
}
}
private bool TryPatchEmoteDigitBindings()
{
try
{
if ((Object)(object)IngamePlayerSettings.Instance == (Object)null)
{
return false;
}
if ((Object)(object)IngamePlayerSettings.Instance.playerInput == (Object)null)
{
return false;
}
InputActionAsset actions = IngamePlayerSettings.Instance.playerInput.actions;
if ((Object)(object)actions == (Object)null)
{
return false;
}
InputAction action = actions.FindAction("Emote1", false);
InputAction action2 = actions.FindAction("Emote2", false);
bool flag = PatchDigitBindings(action, 1);
bool flag2 = PatchDigitBindings(action2, 2);
if (DebugLogs.Value)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)$"Patched emote digit bindings: Emote1={flag}, Emote2={flag2}");
}
return flag || flag2;
}
catch (Exception ex)
{
if (DebugLogs.Value)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)ex);
}
return false;
}
}
private static bool PatchDigitBindings(InputAction action, int digit)
{
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: 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)
if (action == null)
{
return false;
}
bool result = false;
for (int i = 0; i < action.bindings.Count; i++)
{
InputBinding val = action.bindings[i];
if (!((InputBinding)(ref val)).isComposite && !((InputBinding)(ref val)).isPartOfComposite)
{
string text = ((InputBinding)(ref val)).effectivePath ?? ((InputBinding)(ref val)).path ?? "";
string text2 = ((InputBinding)(ref val)).groups ?? "";
if ((text2.Contains("Keyboard") || text.Contains("<Keyboard>")) && (IsDigitBinding(text, digit) || IsNumpadDigitBinding(text, digit)))
{
InputActionRebindingExtensions.ApplyBindingOverride(action, i, "<Keyboard>/f24");
result = true;
}
}
}
if (!action.enabled)
{
action.Enable();
}
return result;
}
private static bool IsDigitBinding(string path, int digit)
{
if (string.IsNullOrEmpty(path))
{
return false;
}
if (!path.Contains("<Keyboard>"))
{
return false;
}
return path.EndsWith("/" + digit, StringComparison.OrdinalIgnoreCase) || path.IndexOf("digit" + digit, StringComparison.OrdinalIgnoreCase) >= 0;
}
private static bool IsNumpadDigitBinding(string path, int digit)
{
if (string.IsNullOrEmpty(path))
{
return false;
}
if (!path.Contains("<Keyboard>"))
{
return false;
}
return path.IndexOf("numpad" + digit, StringComparison.OrdinalIgnoreCase) >= 0 || path.IndexOf("keypad" + digit, StringComparison.OrdinalIgnoreCase) >= 0;
}
private void TryEmote(PlayerControllerB p, int emoteId)
{
if ((Object)(object)p == (Object)null)
{
return;
}
bool flag = ReadBool(p, _fiIsTestingPlayer);
if ((!((NetworkBehaviour)p).IsOwner || !p.isPlayerControlled || (((NetworkBehaviour)p).IsServer && !p.isHostPlayerObject)) && !flag)
{
return;
}
if (_miCheckConditionsForEmote != null)
{
bool flag2 = false;
try
{
flag2 = (bool)_miCheckConditionsForEmote.Invoke(p, null);
}
catch
{
flag2 = true;
}
if (!flag2)
{
return;
}
}
if (_fiTimeSinceStartingEmote != null)
{
float num = ReadFloat(p, _fiTimeSinceStartingEmote, 999f);
if (num < 0.5f)
{
return;
}
WriteFloat(p, _fiTimeSinceStartingEmote, 0f);
}
p.performingEmote = true;
if ((Object)(object)p.playerBodyAnimator != (Object)null)
{
p.playerBodyAnimator.SetInteger("emoteNumber", emoteId);
}
p.StartPerformingEmoteServerRpc();
}
private void SwitchSlotExact(PlayerControllerB p, int targetSlot)
{
if ((Object)(object)p == (Object)null || p.ItemSlots == null || p.ItemSlots.Length == 0)
{
return;
}
int num = p.ItemSlots.Length;
if (targetSlot < 0 || targetSlot >= num)
{
return;
}
bool flag = ReadBool(p, _fiIsTestingPlayer);
if ((!((NetworkBehaviour)p).IsOwner || !p.isPlayerControlled || (((NetworkBehaviour)p).IsServer && !p.isHostPlayerObject)) && !flag)
{
return;
}
ShipBuildModeManager.Instance.CancelBuildMode(true);
if ((Object)(object)p.playerBodyAnimator != (Object)null)
{
p.playerBodyAnimator.SetBool("GrabValidated", false);
}
int currentItemSlot = p.currentItemSlot;
if (currentItemSlot == targetSlot)
{
return;
}
if (_miSwitchToItemSlot != null)
{
ParameterInfo[] parameters = _miSwitchToItemSlot.GetParameters();
try
{
if (parameters.Length == 2)
{
_miSwitchToItemSlot.Invoke(p, new object[2] { targetSlot, null });
}
else
{
_miSwitchToItemSlot.Invoke(p, new object[1] { targetSlot });
}
}
catch (Exception ex)
{
if (DebugLogs.Value)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)ex);
}
}
}
if (_miSwitchToItemSlotServerRpcInt != null)
{
try
{
_miSwitchToItemSlotServerRpcInt.Invoke(p, new object[1] { targetSlot });
return;
}
catch (Exception ex2)
{
if (DebugLogs.Value)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)ex2);
}
}
}
if (_miSwitchItemSlotsServerRpcBool != null && _miNextItemSlotBool != null)
{
int num2 = (targetSlot - currentItemSlot + num) % num;
int num3 = (currentItemSlot - targetSlot + num) % num;
bool flag2 = num2 <= num3;
int num4 = Math.Min(num2, num3);
try
{
for (int i = 0; i < num4; i++)
{
_miSwitchItemSlotsServerRpcBool.Invoke(p, new object[1] { flag2 });
}
}
catch (Exception ex3)
{
if (DebugLogs.Value)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)ex3);
}
}
}
if (InstantSlotSwitch.Value)
{
ForceSlotCooldown(p, SlotSwitchCooldownSeconds.Value);
}
}
}