Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of InstantItemSlots v15.4.2026
BepInEx/plugins/InstantItemSlots/InstantItemSlots.dll
Decompiled 2 months agousing 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; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("InstantItemSlots")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("InstantItemSlots")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4cc5e582-3cd6-41fb-87fe-c92a18523560")] [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", "15.04.2026")] public class InstantItemSlotsPlugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static bool DebugEnabled; internal static bool ArrowEmotesOnly; internal static int ControlledSlots; internal static float CooldownSeconds; internal static float SpawnDelaySeconds; internal static bool IgnoreBusyDuringSpawnDelay; internal static bool IgnoreBusyChecksAlways; internal static bool PlayGrabSfx; internal static bool SyncStepsFallback; private static Harmony _harmony; private static ConfigEntry<bool> _debug; private static ConfigEntry<bool> _arrowEmotesOnly; 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> _playGrabSfx; private static ConfigEntry<bool> _syncStepsFallback; private void Awake() { //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _debug = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableDebugLogging", false, "Enables debug logging."); _arrowEmotesOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Emotes", "UseArrowEmotesOnly", true, "UpArrow = emote 1, DownArrow = emote 2. Vanilla emote input is blocked while enabled."); _controlledSlots = ((BaseUnityPlugin)this).Config.Bind<int>("Hotbar", "DigitControlledSlots", 4, "How many slots are controlled by 1/2/3/4 (1..4)."); _cooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Hotbar", "DigitSwitchCooldownSeconds", 0f, "Minimum time since last switch allowed."); _spawnDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Spawn", "RelaxedRulesWindowSeconds", 0f, "Seconds after level load where switching rules can be relaxed."); _ignoreBusyDuringSpawnDelay = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawn", "IgnoreBusyChecksInsideRelaxedWindow", true, "Ignore animation/interact/activatingItem/twoHanded during the relaxed window."); _ignoreBusyChecksAlways = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawn", "IgnoreBusyChecksAlways", false, "Always ignore animation/interact/activatingItem/twoHanded checks."); _playGrabSfx = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "PlayGrabSoundOnDigitSwitch", true, "Play grab sound when switching with digits."); _syncStepsFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Network", "UseLegacyStepRpcFallback", true, "Fallback sync for old versions without direct slot RPC."); ApplyConfig(); HookConfigChanges(); GameAccess.Init(); _harmony = new Harmony("kviks.instantitemslots"); _harmony.PatchAll(); if (DebugEnabled) { Log.LogInfo((object)"[IIS] Loaded."); } } private void ApplyConfig() { DebugEnabled = _debug.Value; ArrowEmotesOnly = _arrowEmotesOnly.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; PlayGrabSfx = _playGrabSfx.Value; SyncStepsFallback = _syncStepsFallback.Value; } private void HookConfigChanges() { _debug.SettingChanged += delegate { ApplyConfig(); }; _arrowEmotesOnly.SettingChanged += delegate { ApplyConfig(); }; _controlledSlots.SettingChanged += delegate { ApplyConfig(); }; _cooldownSeconds.SettingChanged += delegate { ApplyConfig(); }; _spawnDelaySeconds.SettingChanged += delegate { ApplyConfig(); }; _ignoreBusyDuringSpawnDelay.SettingChanged += delegate { ApplyConfig(); }; _ignoreBusyChecksAlways.SettingChanged += delegate { ApplyConfig(); }; _playGrabSfx.SettingChanged += delegate { ApplyConfig(); }; _syncStepsFallback.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; } if (ArrowEmotesOnly) { if (current.upArrowKey != null && ((ButtonControl)current.upArrowKey).wasPressedThisFrame) { TryPerformArrowEmote(player, 1); return; } if (current.downArrowKey != null && ((ButtonControl)current.downArrowKey).wasPressedThisFrame) { TryPerformArrowEmote(player, 2); 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 TryPerformArrowEmote(PlayerControllerB player, int emoteId) { try { if (!GameAccess.EmoteReady) { if (DebugEnabled) { Log.LogWarning((object)"[IIS] Emote reflection not ready."); } } else if (((((NetworkBehaviour)player).IsOwner && player.isPlayerControlled && (!((NetworkBehaviour)player).IsServer || player.isHostPlayerObject)) || player.isTestingPlayer) && GameAccess.CheckConditionsForEmote(player) && !(GameAccess.GetTimeSinceStartingEmote(player) < 0.5f)) { GameAccess.SetTimeSinceStartingEmote(player, 0f); GameAccess.SetPerformingEmote(player, value: true); if ((Object)(object)player.playerBodyAnimator != (Object)null) { player.playerBodyAnimator.SetInteger("emoteNumber", emoteId); } GameAccess.StartPerformingEmoteServerRpc(player); } } catch (Exception ex) { Log.LogError((object)("[IIS] Emote error: " + ex)); } } 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 || player.twoHanded)) { if (DebugEnabled) { Log.LogInfo((object)"[IIS] Blocked: animation/interact/activatingItem/twoHanded."); } return; } if ((player.jetpackControls || player.disablingJetpackControls) && (Object)(object)player.currentlyHeldObjectServer != (Object)null && (Object)(object)player.currentlyHeldObjectServer.itemProperties != (Object)null && player.currentlyHeldObjectServer.itemProperties.itemId == 13) { 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); if (player.currentItemSlot != targetSlot) { ShipBuildModeManager instance = ShipBuildModeManager.Instance; if ((Object)(object)instance != (Object)null && instance.InBuildMode) { instance.CancelBuildMode(true); } if ((Object)(object)player.playerBodyAnimator != (Object)null) { player.playerBodyAnimator.SetBool("GrabValidated", false); } if (GameAccess.HasDirectSlotRpc) { GameAccess.SwitchToItemSlot(player, targetSlot); GameAccess.SwitchToSlotServerRpc(player, targetSlot); } else { DoLegacyStepSwitch(player, targetSlot, num); } PlayGrabSound(player); GameAccess.SetTimeSinceSwitchingSlots(player, 0f); } } catch (Exception ex) { Log.LogError((object)("[IIS] Switch error: " + ex)); } } private static void DoLegacyStepSwitch(PlayerControllerB player, int targetSlot, int len) { if (!SyncStepsFallback) { GameAccess.SwitchToItemSlot(player, targetSlot); return; } int currentItemSlot = player.currentItemSlot; int num = (targetSlot - currentItemSlot + len) % len; int num2 = (currentItemSlot - targetSlot + len) % len; bool flag = num < num2; int num3 = (flag ? num : num2); if (num3 <= 0 && currentItemSlot != targetSlot) { flag = true; num3 = num; } for (int i = 0; i < num3; i++) { int slot = (flag ? ((player.currentItemSlot + 1) % len) : ((player.currentItemSlot == 0) ? (len - 1) : (player.currentItemSlot - 1))); GameAccess.SwitchToItemSlot(player, slot); GameAccess.SwitchItemSlotsServerRpc(player, flag); } } private static void PlayGrabSound(PlayerControllerB player) { 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; internal static bool EmoteReady; private static MethodInfo _switchToItemSlot; private static MethodInfo _switchToSlotServerRpc; private static MethodInfo _switchItemSlotsServerRpc; private static MethodInfo _checkConditionsForEmote; private static MethodInfo _startPerformingEmoteServerRpc; private static FieldInfo _throwingObject; private static FieldInfo _timeSinceSwitchingSlots; private static FieldInfo _timeSinceStartingEmote; private static FieldInfo _performingEmote; internal static bool HasDirectSlotRpc => _switchToSlotServerRpc != null; internal static void Init() { try { Type typeFromHandle = typeof(PlayerControllerB); _switchToItemSlot = AccessTools.Method(typeFromHandle, "SwitchToItemSlot", new Type[2] { typeof(int), typeof(GrabbableObject) }, (Type[])null); _switchToSlotServerRpc = AccessTools.Method(typeFromHandle, "SwitchToSlotServerRpc", new Type[1] { typeof(int) }, (Type[])null); _switchItemSlotsServerRpc = AccessTools.Method(typeFromHandle, "SwitchItemSlotsServerRpc", new Type[1] { typeof(bool) }, (Type[])null); _throwingObject = AccessTools.Field(typeFromHandle, "throwingObject"); _timeSinceSwitchingSlots = AccessTools.Field(typeFromHandle, "timeSinceSwitchingSlots"); _checkConditionsForEmote = AccessTools.Method(typeFromHandle, "CheckConditionsForEmote", (Type[])null, (Type[])null); _startPerformingEmoteServerRpc = AccessTools.Method(typeFromHandle, "StartPerformingEmoteServerRpc", (Type[])null, (Type[])null); _timeSinceStartingEmote = AccessTools.Field(typeFromHandle, "timeSinceStartingEmote"); _performingEmote = AccessTools.Field(typeFromHandle, "performingEmote"); Ready = _switchToItemSlot != null && _throwingObject != null && _timeSinceSwitchingSlots != null && (_switchToSlotServerRpc != null || _switchItemSlotsServerRpc != null); EmoteReady = _checkConditionsForEmote != null && _startPerformingEmoteServerRpc != null && _timeSinceStartingEmote != null && _performingEmote != null; if (InstantItemSlotsPlugin.DebugEnabled && InstantItemSlotsPlugin.Log != null) { InstantItemSlotsPlugin.Log.LogInfo((object)("[IIS] Reflection: SwitchToItemSlot=" + (_switchToItemSlot != null) + " SwitchToSlotServerRpc=" + (_switchToSlotServerRpc != null) + " SwitchItemSlotsServerRpc=" + (_switchItemSlotsServerRpc != null) + " CheckConditionsForEmote=" + (_checkConditionsForEmote != null) + " StartPerformingEmoteServerRpc=" + (_startPerformingEmoteServerRpc != null))); } } catch (Exception ex) { Ready = false; EmoteReady = 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 SwitchToSlotServerRpc(PlayerControllerB player, int slot) { _switchToSlotServerRpc?.Invoke(player, new object[1] { slot }); } 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 bool CheckConditionsForEmote(PlayerControllerB player) { try { return (bool)_checkConditionsForEmote.Invoke(player, null); } catch { return false; } } internal static float GetTimeSinceStartingEmote(PlayerControllerB player) { try { return (float)_timeSinceStartingEmote.GetValue(player); } catch { return -1f; } } internal static void SetTimeSinceStartingEmote(PlayerControllerB player, float value) { try { _timeSinceStartingEmote.SetValue(player, value); } catch { } } internal static void SetPerformingEmote(PlayerControllerB player, bool value) { try { _performingEmote.SetValue(player, value); } catch { } } internal static void StartPerformingEmoteServerRpc(PlayerControllerB player) { _startPerformingEmoteServerRpc?.Invoke(player, null); } } [HarmonyPatch] internal static class Patches { [HarmonyPatch(typeof(PlayerControllerB), "Update")] [HarmonyPostfix] private static void PlayerUpdatePostfix(PlayerControllerB __instance) { InstantItemSlotsPlugin.HandleLocalUpdate(__instance); } [HarmonyPatch(typeof(PlayerControllerB), "PerformEmote")] [HarmonyPrefix] private static bool PerformEmotePrefix() { return !InstantItemSlotsPlugin.ArrowEmotesOnly; } }