using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using TMPro; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ExtraInventorySlots")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+86a34959c3a3098544184b63ef4eb03edbe7dce4")] [assembly: AssemblyProduct("ExtraInventorySlots")] [assembly: AssemblyTitle("ExtraInventorySlots")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ExtraInventorySlots { [BepInPlugin("DarkSpider90.ExtraInventorySlots", "Extra Inventory Slots", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { internal const string PluginGuid = "DarkSpider90.ExtraInventorySlots"; internal const string PluginName = "Extra Inventory Slots"; internal const string PluginVersion = "1.0.0"; internal const int VanillaSlotCount = 3; private const int MaxSlotCount = 10; private Harmony _harmony; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static ConfigEntry SlotCount { get; private set; } internal static ConfigEntry HostProtection { get; private set; } internal static ConfigEntry KeepItemsInTruck { get; private set; } internal static ConfigEntry ExtraHotkeys { get; private set; } internal static ConfigEntry NumpadHotkeys { get; private set; } internal static ConfigEntry AutoSwapItems { get; private set; } internal static int EffectiveSlotCount => Mathf.Clamp(SlotCount?.Value ?? 3, 3, 10); private void Awake() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; SlotCount = ((BaseUnityPlugin)this).Config.Bind("General", "Number Of Slots", 5, new ConfigDescription("Total inventory slots to show and use. Vanilla is 3. Changes after a scene or round reload.", (AcceptableValueBase)(object)new AcceptableValueRange(3, 10), Array.Empty())); HostProtection = ((BaseUnityPlugin)this).Config.Bind("General", "Host Protection", true, "If true, a host with this mod blocks clients from equipping items into slots above the host's configured slot count."); KeepItemsInTruck = ((BaseUnityPlugin)this).Config.Bind("General", "Keep Items In Truck", false, "If true, extra-slot items are not restored back to players when the game rebuilds item ownership between rounds."); AutoSwapItems = ((BaseUnityPlugin)this).Config.Bind("General", "Auto Swap Items", true, "If true, storing a held item into an occupied inventory slot swaps it with the item already in that slot."); ExtraHotkeys = ((BaseUnityPlugin)this).Config.Bind("Controls", "Extra Slot Hotkeys", true, "If true, number keys 4-9 and 0 control extra inventory slots."); NumpadHotkeys = ((BaseUnityPlugin)this).Config.Bind("Controls", "Numpad Hotkeys", true, "If true, numpad keys also control matching extra inventory slots."); _harmony = new Harmony("DarkSpider90.ExtraInventorySlots"); _harmony.PatchAll(); Log.LogInfo((object)"Extra Inventory Slots v1.0.0 loaded for R.E.P.O. v0.4.0."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class AutoItemSwap { private const float EquipWaitTimeout = 0.75f; private static readonly FieldInfo GrabbedPhysGrabObjectField = AccessTools.Field(typeof(PhysGrabber), "grabbedPhysGrabObject"); private static readonly FieldInfo PlayerInputDisableTimerField = AccessTools.Field(typeof(PlayerController), "InputDisableTimer"); private static readonly FieldInfo LastEquipTimeField = AccessTools.Field(typeof(InventorySpot), "lastEquipTime"); private static readonly FieldInfo EquipCooldownField = AccessTools.Field(typeof(InventorySpot), "equipCooldown"); private static readonly FieldInfo HandleInputField = AccessTools.Field(typeof(InventorySpot), "handleInput"); internal static bool PrefixHandleInput(InventorySpot spot) { if (!Plugin.AutoSwapItems.Value || (Object)(object)spot == (Object)null) { return true; } if (ShouldLetVanillaHandle(spot)) { return true; } ItemEquippable heldItem = GetHeldItem(); if ((Object)(object)heldItem == (Object)null || !spot.IsOccupied()) { return true; } ItemEquippable currentItem = spot.CurrentItem; if ((Object)(object)currentItem == (Object)null || (Object)(object)currentItem == (Object)(object)heldItem) { return true; } int inventorySpotIndex = spot.inventorySpotIndex; int ownerViewId = (SemiFunc.IsMultiplayer() ? PhysGrabber.instance.photonView.ViewID : (-1)); SetInputCooldown(spot); currentItem.RequestUnequip(); ((MonoBehaviour)Plugin.Instance).StartCoroutine(EquipWhenSlotIsReady(heldItem, inventorySpotIndex, ownerViewId, currentItem)); return false; } private static bool ShouldLetVanillaHandle(InventorySpot spot) { if (SemiFunc.RunIsArena()) { return true; } if ((Object)(object)PlayerController.instance != (Object)null && PlayerInputDisableTimerField != null && (float)PlayerInputDisableTimerField.GetValue(PlayerController.instance) > 0f) { return true; } return IsInCooldown(spot); } private static bool IsInCooldown(InventorySpot spot) { if (LastEquipTimeField == null || EquipCooldownField == null || HandleInputField == null) { return false; } if ((bool)HandleInputField.GetValue(spot)) { return false; } float num = (float)LastEquipTimeField.GetValue(spot); float num2 = (float)EquipCooldownField.GetValue(spot); return Time.time - num < num2; } private static void SetInputCooldown(InventorySpot spot) { LastEquipTimeField?.SetValue(spot, Time.time); HandleInputField?.SetValue(spot, false); } private static ItemEquippable GetHeldItem() { PhysGrabber instance = PhysGrabber.instance; if ((Object)(object)instance == (Object)null || !instance.grabbed) { return null; } object? obj = GrabbedPhysGrabObjectField?.GetValue(instance); PhysGrabObject val = (PhysGrabObject)((obj is PhysGrabObject) ? obj : null); if (!((Object)(object)val == (Object)null)) { return ((Component)val).GetComponent(); } return null; } private static IEnumerator EquipWhenSlotIsReady(ItemEquippable heldItem, int slotIndex, int ownerViewId, ItemEquippable previousSlotItem) { float timeoutAt = Time.time + 0.75f; while (Time.time < timeoutAt) { InventorySpot spot = GetSpot(slotIndex); if ((Object)(object)spot == (Object)null || !spot.IsOccupied() || (Object)(object)spot.CurrentItem != (Object)(object)previousSlotItem) { break; } yield return null; } if ((Object)(object)heldItem != (Object)null && !heldItem.IsEquipped()) { heldItem.RequestEquip(slotIndex, ownerViewId); } } private static InventorySpot GetSpot(int slotIndex) { if ((Object)(object)Inventory.instance == (Object)null) { return null; } InventorySlotList.EnsureSlots(Inventory.instance, slotIndex + 1); List allSpots = Inventory.instance.GetAllSpots(); if (slotIndex < 0 || slotIndex >= allSpots.Count) { return null; } return allSpots[slotIndex]; } } internal static class ExtraSlotState { private static readonly Dictionary> ServerMonitoredInventoryItems = new Dictionary>(); internal static void TrackInventoryUpdate(string steamId, string itemName, int spot) { if (!SemiFunc.IsMasterClientOrSingleplayer() || spot < 3 || string.IsNullOrEmpty(steamId)) { return; } if (string.IsNullOrEmpty(itemName)) { if (ServerMonitoredInventoryItems.TryGetValue(steamId, out var value)) { value.Remove(spot); if (value.Count == 0) { ServerMonitoredInventoryItems.Remove(steamId); } } } else { if (!ServerMonitoredInventoryItems.TryGetValue(steamId, out var value2)) { value2 = new Dictionary(); ServerMonitoredInventoryItems[steamId] = value2; } value2[spot] = itemName.GetHashCode(); } } internal static bool TryFindItemOwnerAndSpot(int itemHash, out PlayerAvatar owner, out int spot) { owner = null; spot = -1; List list = SemiFunc.PlayerGetList(); if (list == null) { return false; } foreach (PlayerAvatar item in list) { string key = SemiFunc.PlayerGetSteamID(item); if (!ServerMonitoredInventoryItems.TryGetValue(key, out var value)) { continue; } foreach (KeyValuePair item2 in value) { if (item2.Value == itemHash) { owner = item; spot = item2.Key; return true; } } } return false; } internal static void Clear() { ServerMonitoredInventoryItems.Clear(); } } internal static class InventorySlotList { internal static void EnsureSlots(Inventory inventory, int minimumSlotCount = -1) { if (!((Object)(object)inventory == (Object)null)) { List allSpots = inventory.GetAllSpots(); int num = Math.Max(Plugin.EffectiveSlotCount, minimumSlotCount); while (allSpots.Count < num) { allSpots.Add(null); } } } } internal static class InventoryBatteryBinding { private static readonly FieldInfo BatteryVisualLogicField = AccessTools.Field(typeof(InventorySpot), "batteryVisualLogic"); private static readonly FieldInfo BarsField = AccessTools.Field(typeof(BatteryVisualLogic), "bars"); private static readonly FieldInfo TargetScaleField = AccessTools.Field(typeof(BatteryVisualLogic), "targetScale"); private static readonly FieldInfo TargetScaleOriginalField = AccessTools.Field(typeof(BatteryVisualLogic), "targetScaleOriginal"); private static readonly FieldInfo TargetRotationField = AccessTools.Field(typeof(BatteryVisualLogic), "targetRotation"); private static readonly FieldInfo TargetRotationOriginalField = AccessTools.Field(typeof(BatteryVisualLogic), "targetRotationOriginal"); private static readonly FieldInfo TargetPositionField = AccessTools.Field(typeof(BatteryVisualLogic), "targetPosition"); private static readonly FieldInfo TargetPositionOriginalField = AccessTools.Field(typeof(BatteryVisualLogic), "targetPositionOriginal"); private static readonly FieldInfo DoOutroField = AccessTools.Field(typeof(BatteryVisualLogic), "doOutro"); private static readonly FieldInfo SpringScaleField = AccessTools.Field(typeof(BatteryVisualLogic), "springScale"); private static readonly FieldInfo SpringRotationField = AccessTools.Field(typeof(BatteryVisualLogic), "springRotation"); private static readonly FieldInfo SpringPositionField = AccessTools.Field(typeof(BatteryVisualLogic), "springPosition"); private static readonly FieldInfo SpringFloatLastPositionField = AccessTools.Field(typeof(SpringFloat), "lastPosition"); private static readonly FieldInfo SpringVectorLastPositionField = AccessTools.Field(typeof(SpringVector3), "lastPosition"); internal static BatteryVisualLogic Bind(InventorySpot spot, bool activateForVanillaStart) { if ((Object)(object)spot == (Object)null) { return null; } object? obj = BatteryVisualLogicField?.GetValue(spot); BatteryVisualLogic val = (BatteryVisualLogic)((obj is BatteryVisualLogic) ? obj : null); if ((Object)(object)val != (Object)null && ((Component)val).transform.IsChildOf(((Component)spot).transform)) { if (activateForVanillaStart && !((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(true); } return val; } BatteryVisualLogic val2 = ((Component)spot).GetComponentsInChildren(true).FirstOrDefault(); if ((Object)(object)val2 == (Object)null) { return null; } if (activateForVanillaStart && !((Component)val2).gameObject.activeSelf) { ((Component)val2).gameObject.SetActive(true); } BatteryVisualLogicField?.SetValue(spot, val2); return val2; } internal static void PrepareFreshClone(InventorySpot spot, BatteryVisualLogic templateVisual) { BatteryVisualLogic val = Bind(spot, activateForVanillaStart: true); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)$"Inventory slot {((spot != null) ? new int?(spot.inventorySpotIndex + 1) : null)} has no BatteryVisualLogic child."); return; } BarsField?.SetValue(val, new List()); ResetCloneTargets(val, templateVisual); ResetSprings(val, GetTargetScale(val, templateVisual)); } internal static void Refresh(InventorySpot spot) { BatteryVisualLogic val = Bind(spot, activateForVanillaStart: false); if ((Object)(object)val == (Object)null) { return; } ItemEquippable currentItem = spot.CurrentItem; ItemBattery val2 = (((Object)(object)currentItem == (Object)null) ? null : ((Component)currentItem).GetComponent()); if (!((Object)(object)val2 == (Object)null)) { val.itemBattery = val2; if (!((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(true); } val.ResetOutro(); val.BatteryBarsSet(); val.BatteryBarsUpdate(-1, true); } } private static void ResetCloneTargets(BatteryVisualLogic visual, BatteryVisualLogic templateVisual) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)visual == (Object)null)) { Vector3 targetPosition = GetTargetPosition(visual, templateVisual); float targetScale = GetTargetScale(visual, templateVisual); float fieldValue = GetFieldValue(TargetRotationOriginalField, templateVisual, 0f); ((Component)visual).transform.localPosition = targetPosition; ((Component)visual).transform.localScale = new Vector3(targetScale, targetScale, targetScale); ((Component)visual).transform.localRotation = Quaternion.Euler(0f, 0f, fieldValue); TargetPositionField?.SetValue(visual, targetPosition); TargetPositionOriginalField?.SetValue(visual, targetPosition); TargetScaleField?.SetValue(visual, targetScale); TargetScaleOriginalField?.SetValue(visual, targetScale); TargetRotationField?.SetValue(visual, fieldValue); TargetRotationOriginalField?.SetValue(visual, fieldValue); DoOutroField?.SetValue(visual, false); } } private static Vector3 GetTargetPosition(BatteryVisualLogic visual, BatteryVisualLogic templateVisual) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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) //IL_003d: Unknown result type (might be due to invalid IL or missing references) Vector3 fieldValue = GetFieldValue(TargetPositionOriginalField, templateVisual, ((Component)visual).transform.localPosition); if (float.IsNaN(fieldValue.x) || float.IsInfinity(fieldValue.x)) { return ((Component)visual).transform.localPosition; } return fieldValue; } private static float GetTargetScale(BatteryVisualLogic visual, BatteryVisualLogic templateVisual) { //IL_003c: 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) float fieldValue = GetFieldValue(TargetScaleOriginalField, templateVisual, 0f); if (fieldValue > 0.001f) { return fieldValue; } fieldValue = GetFieldValue(TargetScaleField, templateVisual, 0f); if (fieldValue > 0.001f) { return fieldValue; } fieldValue = Mathf.Max(((Component)visual).transform.localScale.x, ((Component)visual).transform.localScale.y); if (!(fieldValue > 0.001f)) { return 0.5f; } return fieldValue; } private static T GetFieldValue(FieldInfo field, object instance, T fallback) { if (field == null || instance == null) { return fallback; } object value = field.GetValue(instance); if (value is T) { return (T)value; } return fallback; } private static void ResetSprings(BatteryVisualLogic visual, float targetScale) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) SpringFloat val = new SpringFloat { damping = 0.4f, speed = 30f }; SpringFloatLastPositionField?.SetValue(val, targetScale); SpringScaleField?.SetValue(visual, val); SpringFloat val2 = new SpringFloat { damping = 0.3f, speed = 40f }; SpringFloatLastPositionField?.SetValue(val2, 0f); SpringRotationField?.SetValue(visual, val2); SpringVector3 val3 = new SpringVector3 { damping = 0.35f, speed = 30f }; SpringVectorLastPositionField?.SetValue(val3, ((Component)visual).transform.localPosition); SpringPositionField?.SetValue(visual, val3); } } internal static class InventoryUiBuilder { private static readonly FieldInfo AllChildrenField = AccessTools.Field(typeof(SemiUI), "allChildren"); private static readonly FieldInfo CurrentStateField = AccessTools.Field(typeof(InventorySpot), "currentState"); private static readonly FieldInfo CurrentItemBackingField = AccessTools.Field(typeof(InventorySpot), "k__BackingField"); private static readonly FieldInfo StateStartField = AccessTools.Field(typeof(InventorySpot), "stateStart"); internal static void Rebuild(InventoryUI inventoryUi) { //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)inventoryUi == (Object)null) { return; } int effectiveSlotCount = Plugin.EffectiveSlotCount; if (effectiveSlotCount <= 3) { return; } try { Transform transform = ((Component)inventoryUi).transform; List list = (from spot in ((Component)transform).GetComponentsInChildren(true) orderby spot.inventorySpotIndex, ((Object)spot).name select spot).ToList(); InventorySpot val = ((IEnumerable)list).FirstOrDefault((Func)((InventorySpot spot) => spot.inventorySpotIndex == 0)) ?? list.FirstOrDefault(); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"Could not find an InventorySpot template in InventoryUI."); return; } List list2 = AllChildrenField?.GetValue(inventoryUi) as List; float num = CalculateSpacing(list); float num2 = CalculateCenterX(list, ((Component)val).transform.localPosition.x) - num * (float)(effectiveSlotCount - 1) * 0.5f; float y = ((Component)val).transform.localPosition.y; float z = ((Component)val).transform.localPosition.z; for (int i = 0; i < effectiveSlotCount; i++) { InventorySpot orCreateSpot = GetOrCreateSpot(transform, val, list, i); if (!((Object)(object)orCreateSpot == (Object)null)) { ConfigureSpot(orCreateSpot, i, num2 + num * (float)i, y, z); GameObject gameObject = ((Component)orCreateSpot).gameObject; if (list2 != null && !list2.Contains(gameObject)) { list2.Add(gameObject); } } } InventorySlotList.EnsureSlots(Inventory.instance, effectiveSlotCount); } catch (Exception arg) { Plugin.Log.LogWarning((object)$"Failed to rebuild inventory slots: {arg}"); } } private static InventorySpot GetOrCreateSpot(Transform root, InventorySpot template, List existingSpots, int index) { InventorySpot val = ((IEnumerable)existingSpots).FirstOrDefault((Func)((InventorySpot spot) => spot.inventorySpotIndex == index)); if ((Object)(object)val != (Object)null) { return val; } Transform val2 = root.Find($"Inventory Spot {index + 1}"); InventorySpot val3 = default(InventorySpot); if ((Object)(object)val2 != (Object)null && ((Component)val2).TryGetComponent(ref val3)) { if (!existingSpots.Contains(val3)) { existingSpots.Add(val3); } return val3; } Transform obj = Object.Instantiate(((Component)template).transform, ((Component)template).transform.parent); ((Object)obj).name = $"Inventory Spot {index + 1}"; InventorySpot component = ((Component)obj).GetComponent(); BatteryVisualLogic templateVisual = ((Component)template).GetComponentsInChildren(true).FirstOrDefault(); InventoryBatteryBinding.PrepareFreshClone(component, templateVisual); ClearCopiedRuntimeState(component); existingSpots.Add(component); return component; } private static void ClearCopiedRuntimeState(InventorySpot spot) { if (!((Object)(object)spot == (Object)null)) { CurrentItemBackingField?.SetValue(spot, null); if (CurrentStateField != null) { CurrentStateField.SetValue(spot, Enum.ToObject(CurrentStateField.FieldType, 0)); } StateStartField?.SetValue(spot, true); } } private static void ConfigureSpot(InventorySpot spot, int index, float x, float y, float z) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) spot.inventorySpotIndex = index; ((Object)spot).name = $"Inventory Spot {index + 1}"; ((Component)spot).transform.localPosition = new Vector3(x, y, z); ((Component)spot).gameObject.SetActive(true); InventoryBatteryBinding.Bind(spot, activateForVanillaStart: false); string slotLabel = GetSlotLabel(index); if ((Object)(object)spot.noItem != (Object)null) { ((TMP_Text)spot.noItem).text = slotLabel; } Transform val = ((Component)spot).transform.Find("Numbers"); TextMeshProUGUI val2 = default(TextMeshProUGUI); if ((Object)(object)val != (Object)null && ((Component)val).TryGetComponent(ref val2)) { ((TMP_Text)val2).text = slotLabel; } } private static float CalculateSpacing(List spots) { List list = (from spot in spots where spot.inventorySpotIndex >= 0 && spot.inventorySpotIndex < 3 orderby spot.inventorySpotIndex select ((Component)spot).transform.localPosition.x).ToList(); if (list.Count >= 2) { float num = Mathf.Abs(list[1] - list[0]); if (num > 1f) { return num; } } return 40f; } private static float CalculateCenterX(List spots, float fallback) { List list = (from spot in spots where spot.inventorySpotIndex >= 0 && spot.inventorySpotIndex < 3 orderby spot.inventorySpotIndex select ((Component)spot).transform.localPosition.x).ToList(); if (list.Count >= 3) { return (list[0] + list[2]) * 0.5f; } return fallback; } private static string GetSlotLabel(int index) { if (index != 9) { return (index + 1).ToString(); } return "0"; } } internal static class ExtraSlotInput { private static readonly MethodInfo HandleInputMethod = AccessTools.Method(typeof(InventorySpot), "HandleInput", (Type[])null, (Type[])null); internal static void HandleExtraSlotHotkey(InventorySpot spot) { if (Plugin.ExtraHotkeys.Value && !((Object)(object)spot == (Object)null)) { int inventorySpotIndex = spot.inventorySpotIndex; if (inventorySpotIndex >= 3 && inventorySpotIndex < Plugin.EffectiveSlotCount && WasSlotKeyPressed(inventorySpotIndex)) { HandleInputMethod?.Invoke(spot, Array.Empty()); } } } private static bool WasSlotKeyPressed(int index) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) Keyboard current = Keyboard.current; if (current == null) { return false; } if (!WasPressed(current, MainKeyForSlot(index))) { if (Plugin.NumpadHotkeys.Value) { return WasPressed(current, NumpadKeyForSlot(index)); } return false; } return true; } private static bool WasPressed(Keyboard keyboard, Key key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) if ((int)key == 0) { return false; } KeyControl val = keyboard[key]; if (val != null) { return ((ButtonControl)val).wasPressedThisFrame; } return false; } private static Key MainKeyForSlot(int index) { return (Key)(index switch { 3 => 44, 4 => 45, 5 => 46, 6 => 47, 7 => 48, 8 => 49, 9 => 50, _ => 0, }); } private static Key NumpadKeyForSlot(int index) { return (Key)(index switch { 3 => 88, 4 => 89, 5 => 90, 6 => 91, 7 => 92, 8 => 93, 9 => 84, _ => 0, }); } } [HarmonyPatch(typeof(Inventory), "Awake")] internal static class InventoryAwakePatch { private static void Postfix(Inventory __instance) { InventorySlotList.EnsureSlots(__instance); } } [HarmonyPatch(typeof(Inventory), "InventorySpotAddAtIndex")] internal static class InventorySpotAddAtIndexPatch { private static void Prefix(Inventory __instance, int index) { InventorySlotList.EnsureSlots(__instance, index + 1); } } [HarmonyPatch(typeof(InventoryUI), "Start")] internal static class InventoryUiStartPatch { private static void Postfix(InventoryUI __instance) { InventoryUiBuilder.Rebuild(__instance); } } [HarmonyPatch(typeof(InventorySpot), "Start")] internal static class InventorySpotStartPatch { private static void Prefix(InventorySpot __instance) { InventoryBatteryBinding.Bind(__instance, activateForVanillaStart: true); } private static void Postfix(InventorySpot __instance) { InventoryBatteryBinding.Refresh(__instance); } } [HarmonyPatch(typeof(InventorySpot), "Update")] internal static class InventorySpotUpdatePatch { private static void Postfix(InventorySpot __instance) { ExtraSlotInput.HandleExtraSlotHotkey(__instance); } } [HarmonyPatch(typeof(InventorySpot), "HandleInput")] internal static class InventorySpotHandleInputPatch { private static bool Prefix(InventorySpot __instance) { return AutoItemSwap.PrefixHandleInput(__instance); } } [HarmonyPatch(typeof(InventorySpot), "EquipItem")] internal static class InventorySpotEquipItemPatch { private static void Postfix(InventorySpot __instance) { InventoryBatteryBinding.Refresh(__instance); } } [HarmonyPatch(typeof(InventorySpot), "UpdateUI")] internal static class InventorySpotUpdateUiPatch { private static void Postfix(InventorySpot __instance) { InventoryBatteryBinding.Refresh(__instance); } } [HarmonyPatch(typeof(InventorySpot), "StateOccupied")] internal static class InventorySpotStateOccupiedPatch { private static void Prefix(InventorySpot __instance) { InventoryBatteryBinding.Bind(__instance, activateForVanillaStart: false); } } [HarmonyPatch(typeof(StatsManager), "PlayerInventoryUpdate")] internal static class StatsManagerPlayerInventoryUpdatePatch { private static void Postfix(string _steamID, string itemName, int spot) { ExtraSlotState.TrackInventoryUpdate(_steamID, itemName, spot); } } [HarmonyPatch(typeof(ItemEquippable), "RPC_RequestEquip")] internal static class ItemEquippableRequestEquipPatch { private static bool Prefix(int spotIndex) { if (IsRestoringFromItemNameLogic() && Plugin.KeepItemsInTruck.Value) { return false; } if (SemiFunc.IsMultiplayer() && Plugin.HostProtection.Value && spotIndex >= Plugin.EffectiveSlotCount) { return false; } return true; } private static bool IsRestoringFromItemNameLogic() { return new StackTrace().GetFrames()?.Any((StackFrame frame) => frame.GetMethod()?.Name == "SetItemNameLOGIC") ?? false; } } [HarmonyPatch(typeof(PunManager), "SetItemNameLOGIC")] internal static class PunManagerSetItemNameLogicPatch { private static void Postfix(string _name, int photonViewID, ItemAttributes _itemAttributes) { if (Plugin.KeepItemsInTruck.Value || string.IsNullOrEmpty(_name)) { return; } try { ItemEquippable val = ResolveItemEquippable(photonViewID, _itemAttributes); if ((Object)(object)val == (Object)null || val.IsEquipped() || !ExtraSlotState.TryFindItemOwnerAndSpot(_name.GetHashCode(), out var owner, out var spot) || spot < 3 || spot >= Plugin.EffectiveSlotCount) { return; } int num = -1; if (SemiFunc.IsMultiplayer()) { if ((Object)(object)owner.photonView == (Object)null) { return; } num = owner.photonView.ViewID; } val.RequestEquip(spot, num); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to restore extra-slot item '" + _name + "': " + ex.Message)); } } private static ItemEquippable ResolveItemEquippable(int photonViewID, ItemAttributes itemAttributes) { ItemAttributes val = itemAttributes; if (SemiFunc.IsMultiplayer()) { PhotonView val2 = PhotonView.Find(photonViewID); if ((Object)(object)val2 == (Object)null) { return null; } val = ((Component)val2).GetComponent(); } if (!((Object)(object)val == (Object)null)) { return ((Component)val).GetComponent(); } return null; } } [HarmonyPatch(typeof(MainMenuOpen), "Start")] internal static class MainMenuOpenStartPatch { private static void Postfix() { ExtraSlotState.Clear(); } } }