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 InventoryControls v1.6.1
BepInEx/plugins/InventoryControls/InventoryControls.dll
Decompiled 4 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.UI; [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("ByteSizeMetal")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) 2026 ByteSizeMetal")] [assembly: AssemblyFileVersion("1.4.0.0")] [assembly: AssemblyInformationalVersion("1.4.0")] [assembly: AssemblyProduct("Inventory Controls")] [assembly: AssemblyTitle("InventoryControls")] [assembly: AssemblyMetadata("Author", "ByteSizeMetal")] [assembly: AssemblyVersion("1.4.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 InventoryControls { public enum ControllerButton { None, DpadLeft, DpadRight, DpadUp, DpadDown, LeftShoulder, RightShoulder, LeftTrigger, RightTrigger, LeftStick, RightStick, South, East, West, North, Start, Select } public enum WheelBinding { None, Up, Down } internal sealed class ActionBinding { private readonly ConfigEntry<KeyCode> _keyboard; private readonly ConfigEntry<KeyCode> _keyModifier; private readonly ConfigEntry<KeyCode> _keyModifier2; private readonly ConfigEntry<ControllerButton> _controller; private readonly ConfigEntry<ControllerButton> _modifier; private readonly ConfigEntry<WheelBinding> _wheel; private KeyCode _mainKey; private KeyCode[] _modifiers = Array.Empty<KeyCode>(); private static readonly Dictionary<KeyCode, Key> KeyCache = new Dictionary<KeyCode, Key>(); internal ActionBinding(ConfigFile config, string name, KeyCode keyboard, ControllerButton controller) { //IL_0024: 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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) string text = "Bindings - " + name; KeyboardShortcut value = config.Bind<KeyboardShortcut>(text, "Keyboard or mouse", new KeyboardShortcut(keyboard, Array.Empty<KeyCode>()), "Legacy shortcut retained for migration. Use Keyboard / Mouse Key and modifier settings instead.").Value; KeyCode[] array = ((KeyboardShortcut)(ref value)).Modifiers.ToArray(); _keyboard = config.Bind<KeyCode>(text, "Keyboard / Mouse Key", ((KeyboardShortcut)(ref value)).MainKey, "Choose a keyboard key or Mouse0 through Mouse4. None disables the keyboard/mouse binding."); _keyModifier = config.Bind<KeyCode>(text, "Keyboard Modifier", (KeyCode)((array.Length != 0) ? ((int)array[0]) : 0), "Optional key that must also be held, for example LeftShift or LeftControl. None means no modifier."); _keyModifier2 = config.Bind<KeyCode>(text, "Keyboard Modifier 2", (KeyCode)((array.Length > 1) ? ((int)array[1]) : 0), "Optional second modifier key for a three-key shortcut."); _controller = config.Bind<ControllerButton>(text, "Controller button", controller, "Choose a controller button. South = Xbox A / PlayStation Cross; East = B / Circle; West = X / Square; North = Y / Triangle. None disables it."); _modifier = config.Bind<ControllerButton>(text, "Controller modifier", ControllerButton.None, "Optional controller button that must also be held."); _wheel = config.Bind<WheelBinding>(text, "Mouse wheel", WheelBinding.None, "Optional mouse-wheel direction. Does not require a keyboard shortcut."); CacheShortcut(); _keyboard.SettingChanged += delegate { CacheShortcut(); }; _keyModifier.SettingChanged += delegate { CacheShortcut(); }; _keyModifier2.SettingChanged += delegate { CacheShortcut(); }; } private void CacheShortcut() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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_0054: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected I4, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected I4, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected I4, but got Unknown //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected I4, but got Unknown _mainKey = _keyboard.Value; if ((int)_keyModifier.Value == 0) { _modifiers = (KeyCode[])(((int)_keyModifier2.Value == 0) ? ((Array)Array.Empty<KeyCode>()) : ((Array)new KeyCode[1] { (KeyCode)(int)_keyModifier2.Value })); return; } _modifiers = (KeyCode[])(object)(((int)_keyModifier2.Value != 0) ? new KeyCode[2] { (KeyCode)(int)_keyModifier.Value, (KeyCode)(int)_keyModifier2.Value } : new KeyCode[1] { (KeyCode)(int)_keyModifier.Value }); } internal bool Pressed() { return Sample().AnyPressed; } internal BindingSample Sample() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) BindingSample result = default(BindingSample); ButtonControl val = KeyButton(_mainKey); if (val != null) { result.Connected |= BindingSource.KeyboardMouse; bool flag = true; for (int i = 0; i < _modifiers.Length; i++) { ButtonControl obj = KeyButton(_modifiers[i]); if (obj == null || !obj.isPressed) { flag = false; break; } } if (flag && val.wasPressedThisFrame) { result.Pressed |= BindingSource.KeyboardMouse; } if (flag && val.isPressed) { result.Held |= BindingSource.KeyboardMouse; } } ButtonControl val2 = PadButton(_controller.Value); int num; if (val2 != null) { result.Connected |= BindingSource.Controller; if (_modifier.Value != ControllerButton.None) { ButtonControl obj2 = PadButton(_modifier.Value); if (obj2 == null) { num = 0; } else { num = (obj2.isPressed ? 1 : 0); if (num != 0) { goto IL_00dc; } } goto IL_00f0; } num = 1; goto IL_00dc; } goto IL_0106; IL_00dc: if (val2.wasPressedThisFrame) { result.Pressed |= BindingSource.Controller; } goto IL_00f0; IL_0106: if (_wheel.Value != WheelBinding.None && Mouse.current != null) { float y = ((InputControl<Vector2>)(object)Mouse.current.scroll).ReadValue().y; if ((_wheel.Value == WheelBinding.Up) ? (y > 0f) : (y < 0f)) { result.WheelPulse = true; } } return result; IL_00f0: if (num != 0 && val2.isPressed) { result.Held |= BindingSource.Controller; } goto IL_0106; } private unsafe static ButtonControl KeyButton(KeyCode code) { //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_000b: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected I4, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Invalid comparison between Unknown and I4 //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Expected I4, but got Unknown //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Expected I4, but got Unknown //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) if ((int)code == 0) { return null; } if ((int)code >= 323 && (int)code <= 327) { Mouse current = Mouse.current; if (current == null) { return null; } return (ButtonControl)((code - 323) switch { 0 => current.leftButton, 1 => current.rightButton, 2 => current.middleButton, 3 => current.backButton, _ => current.forwardButton, }); } Keyboard current2 = Keyboard.current; if (current2 == null) { return null; } if (KeyCache.TryGetValue(code, out var value)) { if ((int)value != 0) { return (ButtonControl)(object)current2[value]; } return null; } string text = ((object)(*(KeyCode*)(&code))/*cast due to .constrained prefix*/).ToString(); if (text.StartsWith("Alpha", StringComparison.Ordinal)) { text = "Digit" + text.Substring(5); } else if (text.StartsWith("Keypad", StringComparison.Ordinal)) { text = "Numpad" + text.Substring(6); } if ((int)code != 13) { switch (code - 266) { default: switch (code - 305) { case 1: text = "LeftCtrl"; break; case 0: text = "RightCtrl"; break; case 5: case 6: text = "LeftMeta"; break; case 4: case 7: text = "RightMeta"; break; } break; case 0: text = "NumpadPeriod"; break; case 1: text = "NumpadDivide"; break; case 2: text = "NumpadMultiply"; break; case 3: text = "NumpadMinus"; break; case 4: text = "NumpadPlus"; break; } } else { text = "Enter"; } if (!Enum.TryParse<Key>(text, ignoreCase: true, out Key result)) { result = (Key)0; } KeyCache[code] = result; if ((int)result == 0) { return null; } return (ButtonControl)(object)current2[result]; } private static ButtonControl PadButton(ControllerButton binding) { Gamepad current = Gamepad.current; if (current == null) { return null; } return (ButtonControl)(binding switch { ControllerButton.DpadLeft => current.dpad.left, ControllerButton.DpadRight => current.dpad.right, ControllerButton.DpadUp => current.dpad.up, ControllerButton.DpadDown => current.dpad.down, ControllerButton.LeftShoulder => current.leftShoulder, ControllerButton.RightShoulder => current.rightShoulder, ControllerButton.LeftTrigger => current.leftTrigger, ControllerButton.RightTrigger => current.rightTrigger, ControllerButton.LeftStick => current.leftStickButton, ControllerButton.RightStick => current.rightStickButton, ControllerButton.South => current.buttonSouth, ControllerButton.East => current.buttonEast, ControllerButton.West => current.buttonWest, ControllerButton.North => current.buttonNorth, ControllerButton.Start => current.startButton, ControllerButton.Select => current.selectButton, _ => null, }); } } public sealed class HotbarHighlight : MaskableGraphic { internal float Thickness = 4f; protected override void OnPopulateMesh(VertexHelper mesh) { //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_0012: 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_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) mesh.Clear(); Rect rect = ((Graphic)this).rectTransform.rect; Rect rect2 = rect; ((Rect)(ref rect2)).xMin = ((Rect)(ref rect2)).xMin - 2f; ((Rect)(ref rect2)).xMax = ((Rect)(ref rect2)).xMax + 2f; ((Rect)(ref rect2)).yMin = ((Rect)(ref rect2)).yMin - 2f; ((Rect)(ref rect2)).yMax = ((Rect)(ref rect2)).yMax + 2f; Border(mesh, rect2, Thickness + 3f, new Color(0.08f, 1f, 0.12f, 0.18f)); Border(mesh, rect, Thickness, new Color(0.15f, 1f, 0.08f, 1f)); ((Rect)(ref rect)).xMin = ((Rect)(ref rect)).xMin + Thickness; ((Rect)(ref rect)).xMax = ((Rect)(ref rect)).xMax - Thickness; ((Rect)(ref rect)).yMin = ((Rect)(ref rect)).yMin + Thickness; ((Rect)(ref rect)).yMax = ((Rect)(ref rect)).yMax - Thickness; Border(mesh, rect, 0.8f, new Color(0.7f, 1f, 0.55f, 0.9f)); } private static void Border(VertexHelper mesh, Rect rect, float width, Color color) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) if (!(((Rect)(ref rect)).width <= 0f) && !(((Rect)(ref rect)).height <= 0f)) { float num = Mathf.Min(width, Mathf.Min(((Rect)(ref rect)).width, ((Rect)(ref rect)).height) * 0.5f); Quad(mesh, ((Rect)(ref rect)).xMin, ((Rect)(ref rect)).yMin, ((Rect)(ref rect)).xMax, ((Rect)(ref rect)).yMin + num, color); Quad(mesh, ((Rect)(ref rect)).xMin, ((Rect)(ref rect)).yMax - num, ((Rect)(ref rect)).xMax, ((Rect)(ref rect)).yMax, color); Quad(mesh, ((Rect)(ref rect)).xMin, ((Rect)(ref rect)).yMin + num, ((Rect)(ref rect)).xMin + num, ((Rect)(ref rect)).yMax - num, color); Quad(mesh, ((Rect)(ref rect)).xMax - num, ((Rect)(ref rect)).yMin + num, ((Rect)(ref rect)).xMax, ((Rect)(ref rect)).yMax - num, color); } } private static void Quad(VertexHelper mesh, float left, float bottom, float right, float top, Color color) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) if (!(right <= left) && !(top <= bottom)) { int currentVertCount = mesh.currentVertCount; mesh.AddVert(new Vector3(left, bottom), Color32.op_Implicit(color), Vector4.op_Implicit(Vector2.zero)); mesh.AddVert(new Vector3(left, top), Color32.op_Implicit(color), Vector4.op_Implicit(Vector2.zero)); mesh.AddVert(new Vector3(right, top), Color32.op_Implicit(color), Vector4.op_Implicit(Vector2.zero)); mesh.AddVert(new Vector3(right, bottom), Color32.op_Implicit(color), Vector4.op_Implicit(Vector2.zero)); mesh.AddTriangle(currentVertCount, currentVertCount + 1, currentVertCount + 2); mesh.AddTriangle(currentVertCount, currentVertCount + 2, currentVertCount + 3); } } } internal static class HotbarLayout { internal const float Available = 660f; internal static float FitPadding(float padding, float width, int count) { if (count < 2 || width <= 660f) { return padding; } float num = padding - (width - 660f) / (2f * (float)(count - 1)); if (!(num > 0f)) { return 0f; } return num; } internal static int Rank(int ordinal, int mainOrdinal, int count) { if (count <= 0 || ordinal < 0 || ordinal >= count) { return -1; } if (mainOrdinal < 0 || mainOrdinal >= count) { return ordinal; } return (ordinal - mainOrdinal + count + (count - 1) / 2) % count; } } internal sealed class HotbarPresentation : IDisposable { private readonly FieldRef<InventorySpot, float> _offset = AccessTools.FieldRefAccess<InventorySpot, float>("layoutOffsetX"); private readonly Action<InventorySpot, float> _setOffset = AccessTools.MethodDelegate<Action<InventorySpot, float>>(AccessTools.DeclaredMethod(typeof(InventorySpot), "LayoutOffsetSet", new Type[1] { typeof(float) }, (Type[])null), (object)null, true); private readonly FieldRef<InventoryUI, InventorySpot[]> _uiSpots = AccessTools.FieldRefAccess<InventoryUI, InventorySpot[]>("inventorySpots"); private readonly FieldRef<InventoryUI, float[]> _slotX = AccessTools.FieldRefAccess<InventoryUI, float[]>("slotPositionsX"); private readonly FieldRef<InventoryUI, float> _center = AccessTools.FieldRefAccess<InventoryUI, float>("slotCenterX"); private readonly FieldRef<InventoryUI, float> _padding = AccessTools.FieldRefAccess<InventoryUI, float>("buttonPadding"); private readonly FieldRef<InventoryUI, string[]> _previous = AccessTools.FieldRefAccess<InventoryUI, string[]>("previousBindings"); private readonly Action<InventoryUI> _setup = AccessTools.MethodDelegate<Action<InventoryUI>>(AccessTools.DeclaredMethod(typeof(InventoryUI), "LayoutSetup", Type.EmptyTypes, (Type[])null), (object)null, true); private readonly InventorySpot[] _spots = (InventorySpot[])(object)new InventorySpot[9]; private readonly float[] _baseOffsets = new float[9]; private readonly float[] _ownOffsets = new float[9]; private readonly float[] _positions = new float[9]; private readonly int[] _indices = new int[9]; private readonly Vector3[] _corners = (Vector3[])(object)new Vector3[4]; private InventoryUI _ui; private InventoryBattery[] _batteries = Array.Empty<InventoryBattery>(); private Vector3[] _batteryOffsets = Array.Empty<Vector3>(); private int _childCount = -1; private int _slotCount = -1; private HotbarHighlight _highlight; private RectTransform _highlightRect; private float _nativePadding; private float _fittedPadding = float.MaxValue; private void Relist(InventoryUI ui, int count) { InventorySpot[] array = _uiSpots.Invoke(ui); bool flag = array == null; if (array != null) { InventorySpot[] array2 = array; for (int i = 0; i < array2.Length; i++) { if ((Object)(object)array2[i] == (Object)null) { flag = true; } } } if (flag || (array.Length != count && ((Component)ui).GetComponentsInChildren<InventorySpot>(true).Length != array.Length)) { _setup(ui); InventorySpot[] array3 = _uiSpots.Invoke(ui); float[] array4 = _slotX.Invoke(ui); float num = 0f; for (int j = 0; j < array3.Length; j++) { array4[j] = ((SemiUI)array3[j]).showPosition.x; num += array4[j]; } _center.Invoke(ui) = ((array3.Length == 0) ? 0f : (num / (float)array3.Length)); } } private void SetPadding(InventoryUI ui, float value) { if (!Mathf.Approximately(_padding.Invoke(ui), value)) { _padding.Invoke(ui) = value; string[] array = _previous.Invoke(ui); if (array != null) { Array.Clear(array, 0, array.Length); } } } internal void RestoreOffsets() { //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < _spots.Length; i++) { InventorySpot val = _spots[i]; if ((Object)(object)val != (Object)null && _ownOffsets[i] != 0f && Mathf.Abs(_offset.Invoke(val) - (_baseOffsets[i] + _ownOffsets[i])) < 0.01f) { _setOffset(val, _baseOffsets[i]); } _ownOffsets[i] = 0f; } for (int j = 0; j < _batteries.Length; j++) { if ((Object)(object)_batteries[j] != (Object)null && _batteryOffsets[j] != Vector3.zero) { Transform transform = ((Component)_batteries[j]).transform; transform.localPosition -= _batteryOffsets[j]; } _batteryOffsets[j] = Vector3.zero; } } internal void Update(NativeInventory inventory, int mainSlot, bool enabled, bool center, bool highlight, float thickness, float padding, float spacing = 0f) { //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_05a0: Unknown result type (might be due to invalid IL or missing references) //IL_05a7: Expected O, but got Unknown //IL_05af: Unknown result type (might be due to invalid IL or missing references) //IL_05b9: Expected O, but got Unknown //IL_063c: Unknown result type (might be due to invalid IL or missing references) //IL_0641: Unknown result type (might be due to invalid IL or missing references) //IL_0646: Unknown result type (might be due to invalid IL or missing references) //IL_0648: Unknown result type (might be due to invalid IL or missing references) //IL_064a: Unknown result type (might be due to invalid IL or missing references) //IL_065f: Unknown result type (might be due to invalid IL or missing references) //IL_0664: Unknown result type (might be due to invalid IL or missing references) //IL_0669: Unknown result type (might be due to invalid IL or missing references) //IL_066b: Unknown result type (might be due to invalid IL or missing references) //IL_066d: Unknown result type (might be due to invalid IL or missing references) //IL_066f: Unknown result type (might be due to invalid IL or missing references) //IL_0674: Unknown result type (might be due to invalid IL or missing references) //IL_0676: Unknown result type (might be due to invalid IL or missing references) //IL_0678: Unknown result type (might be due to invalid IL or missing references) //IL_067a: Unknown result type (might be due to invalid IL or missing references) //IL_067f: Unknown result type (might be due to invalid IL or missing references) //IL_0692: Unknown result type (might be due to invalid IL or missing references) //IL_0694: Unknown result type (might be due to invalid IL or missing references) //IL_0696: Unknown result type (might be due to invalid IL or missing references) //IL_06a0: Unknown result type (might be due to invalid IL or missing references) //IL_06b0: Unknown result type (might be due to invalid IL or missing references) //IL_06b7: Unknown result type (might be due to invalid IL or missing references) //IL_06c8: Unknown result type (might be due to invalid IL or missing references) //IL_06cf: Unknown result type (might be due to invalid IL or missing references) //IL_06e0: Unknown result type (might be due to invalid IL or missing references) //IL_0479: Unknown result type (might be due to invalid IL or missing references) //IL_047e: Unknown result type (might be due to invalid IL or missing references) //IL_0483: Unknown result type (might be due to invalid IL or missing references) //IL_0499: Unknown result type (might be due to invalid IL or missing references) //IL_049b: Unknown result type (might be due to invalid IL or missing references) //IL_04a0: Unknown result type (might be due to invalid IL or missing references) //IL_04ad: Unknown result type (might be due to invalid IL or missing references) //IL_04ba: Unknown result type (might be due to invalid IL or missing references) //IL_04bf: Unknown result type (might be due to invalid IL or missing references) RestoreOffsets(); InventoryUI instance = InventoryUI.instance; if ((Object)(object)_ui != (Object)(object)instance) { if ((Object)(object)_highlight != (Object)null) { Object.Destroy((Object)(object)((Component)_highlight).gameObject); } _highlight = null; _highlightRect = null; _ui = instance; _childCount = (_slotCount = -1); _batteries = Array.Empty<InventoryBattery>(); _batteryOffsets = Array.Empty<Vector3>(); _fittedPadding = float.MaxValue; if ((Object)(object)instance != (Object)null) { _nativePadding = _padding.Invoke(instance); } } if ((Object)(object)instance == (Object)null || !enabled) { if ((Object)(object)instance != (Object)null) { SetPadding(instance, _nativePadding); } if ((Object)(object)_highlight != (Object)null) { ((Behaviour)_highlight).enabled = false; } return; } SetPadding(instance, Mathf.Min(_fittedPadding, _nativePadding + Mathf.Max(0f, spacing) * 0.5f)); bool flag = false; for (int i = 0; i < _spots.Length; i++) { if ((Object)(object)_spots[i] != (Object)(object)inventory.SpotAt(i)) { flag = true; } } Array.Clear(_spots, 0, _spots.Length); int num = 0; int num2 = -1; Transform val = null; bool flag2 = true; for (int j = 0; j < inventory.Count; j++) { InventorySpot val2 = inventory.SpotAt(j); if (!((Object)(object)val2 == (Object)null)) { _spots[j] = val2; _indices[num] = j; _baseOffsets[j] = _offset.Invoke(val2); _positions[num] = ((SemiUI)val2).showPosition.x + _baseOffsets[j]; if (num == 0) { val = ((Component)val2).transform.parent; } else if ((Object)(object)((Component)val2).transform.parent != (Object)(object)val) { flag2 = false; } if (j == mainSlot) { num2 = num; } num++; } } int childCount = ((Component)instance).transform.childCount; if (_childCount != childCount || _slotCount != num || flag) { Relist(instance, num); _batteries = ((Component)instance).GetComponentsInChildren<InventoryBattery>(true); _batteryOffsets = (Vector3[])(object)new Vector3[_batteries.Length]; _childCount = childCount; _slotCount = num; } if (num > 1) { InventorySpot[] obj = _uiSpots.Invoke(instance); if (obj != null && obj.Length == num) { float num3 = float.MaxValue; float num4 = float.MinValue; for (int k = 0; k < num; k++) { num3 = Mathf.Min(num3, _positions[k]); num4 = Mathf.Max(num4, _positions[k]); } float num5 = num4 - num3; Transform transform = ((Component)_spots[_indices[0]]).transform; Transform obj2 = ((transform is RectTransform) ? transform : null); float num6; if (obj2 == null) { num6 = 0f; } else { Rect rect = ((RectTransform)obj2).rect; num6 = ((Rect)(ref rect)).width; } float width = num5 + num6; float num7 = HotbarLayout.FitPadding(_padding.Invoke(instance), width, num); if (num7 < _padding.Invoke(instance)) { _fittedPadding = num7; } } } if (center && num2 >= 0 && flag2) { Array.Sort(_positions, 0, num); for (int l = 0; l < num; l++) { int num8 = _indices[l]; InventorySpot val3 = _spots[num8]; float num9 = ((SemiUI)val3).showPosition.x + _baseOffsets[num8]; _ownOffsets[num8] = _positions[HotbarLayout.Rank(l, num2, num)] - num9; _setOffset(val3, _baseOffsets[num8] + _ownOffsets[num8]); } for (int m = 0; m < _batteries.Length; m++) { InventoryBattery val4 = _batteries[m]; if (!((Object)(object)val4 == (Object)null) && !((Object)(object)((Component)val4).GetComponentInParent<InventorySpot>() != (Object)null)) { int inventorySpot = val4.inventorySpot; if (inventorySpot >= 0 && inventorySpot < _spots.Length && !((Object)(object)_spots[inventorySpot] == (Object)null) && !((Object)(object)((Component)val4).transform.parent == (Object)null)) { Vector3 val5 = ((Component)_spots[inventorySpot]).transform.parent.TransformVector(new Vector3(_ownOffsets[inventorySpot], 0f, 0f)); _batteryOffsets[m] = ((Component)val4).transform.parent.InverseTransformVector(val5); Transform transform2 = ((Component)val4).transform; transform2.localPosition += _batteryOffsets[m]; } } } } InventorySpot val6 = ((mainSlot >= 0 && mainSlot < _spots.Length) ? _spots[mainSlot] : null); if (!highlight || !inventory.CanAct || ((SemiUI)instance).isHidden || !((Object)(object)val6 != (Object)null) || !((Component)val6).gameObject.activeInHierarchy) { if ((Object)(object)_highlight != (Object)null) { ((Behaviour)_highlight).enabled = false; } return; } Transform transform3 = ((Component)val6).transform; RectTransform val7 = (RectTransform)(object)((transform3 is RectTransform) ? transform3 : null); if (!((Object)(object)val7 == (Object)null)) { if ((Object)(object)_highlight == (Object)null) { GameObject val8 = new GameObject("Inventory Controls Main Item", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(HotbarHighlight) }); _highlightRect = (RectTransform)val8.transform; ((Transform)_highlightRect).SetParent(((Component)instance).transform, false); _highlight = val8.GetComponent<HotbarHighlight>(); ((Graphic)_highlight).raycastTarget = false; val8.AddComponent<LayoutElement>().ignoreLayout = true; } ((Behaviour)_highlight).enabled = true; if (((Transform)_highlightRect).GetSiblingIndex() != ((Component)instance).transform.childCount - 1) { ((Transform)_highlightRect).SetAsLastSibling(); } val7.GetWorldCorners(_corners); Vector3 val9 = ((Component)instance).transform.InverseTransformPoint(_corners[0]); Vector3 val10 = val9; for (int n = 1; n < 4; n++) { Vector3 val11 = ((Component)instance).transform.InverseTransformPoint(_corners[n]); val9 = Vector3.Min(val9, val11); val10 = Vector3.Max(val10, val11); } ((Transform)_highlightRect).localPosition = (val9 + val10) * 0.5f; _highlightRect.sizeDelta = new Vector2(val10.x - val9.x + 2f * padding, val10.y - val9.y + 2f * padding); if (!Mathf.Approximately(_highlight.Thickness, thickness)) { _highlight.Thickness = thickness; ((Graphic)_highlight).SetVerticesDirty(); } } } public void Dispose() { RestoreOffsets(); if ((Object)(object)_ui != (Object)null) { SetPadding(_ui, _nativePadding); } if ((Object)(object)_highlight != (Object)null) { Object.Destroy((Object)(object)((Component)_highlight).gameObject); } _highlight = null; _highlightRect = null; } } internal static class InventoryCapacity { internal static int Limit(bool slrDetected, bool inventoryUpgradeEnabled, int upgradeLevel) { if (!(slrDetected && inventoryUpgradeEnabled)) { return 3; } return 3 + Math.Min(6, Math.Max(0, upgradeLevel)); } } internal sealed class InventoryCommands { private enum Phase { Idle, Storing, Drawing } private Phase _phase; private object _identity; private readonly Queue<int> _steps = new Queue<int>(); private bool _putAway; private int _dropItem; private int _putAwayItem; private int _source; private int _target; private int _targetSlot = -1; private int _cursor = -1; private int _lastItem; private int _directSlot = -1; private float _deadline; private float _queuedUntil; public float Timeout = 2f; public string LastResult { get; private set; } public int MainSlot { get; private set; } = -1; public void Cancel() { _steps.Clear(); _putAway = false; _phase = Phase.Idle; _source = (_target = (_dropItem = (_putAwayItem = 0))); _targetSlot = (_directSlot = -1); } public void SelectSlot(int slot, float now) { StopCycling(); _directSlot = slot; _queuedUntil = now + Timeout * 2f; } public void StopCycling() { _steps.Clear(); _putAway = false; _dropItem = (_putAwayItem = 0); _directSlot = -1; if (_phase == Phase.Storing) { _target = 0; } } public void Tick(IInventoryView view, float now, int direction, bool putAway, int dropItem = 0, int putAwayItem = 0) { if (_identity != view.Identity) { Cancel(); int cursor = (MainSlot = -1); _cursor = cursor; _lastItem = 0; _identity = view.Identity; } if (!view.CanAct) { Cancel(); return; } ObserveSelection(view); EnsureReadySelection(view); if (dropItem != 0) { _steps.Clear(); _putAway = false; _putAwayItem = 0; _dropItem = dropItem; _directSlot = -1; if (_phase == Phase.Storing) { _target = 0; } _queuedUntil = now + Timeout; } else if (putAway) { _steps.Clear(); _putAway = true; _putAwayItem = ((putAwayItem != 0) ? putAwayItem : view.Held); _dropItem = 0; _directSlot = -1; if (_phase == Phase.Storing) { _target = 0; } _queuedUntil = now + Timeout; } else if (direction != 0 && _steps.Count < 16) { _steps.Enqueue(Math.Sign(direction)); _queuedUntil = now + Timeout * 2f; _directSlot = -1; } if (_phase != Phase.Idle && now > _deadline) { LastResult = "Timed out waiting for the game's inventory animation"; Cancel(); return; } if (_phase == Phase.Storing) { if (!view.StoredAndSettled(_source) || view.Held == _source) { return; } if (view.Held != 0) { LastResult = "Manual item change interrupted cycling"; Cancel(); return; } _phase = Phase.Idle; if (_target != 0 && !_putAway && _dropItem == 0) { if (view.Available(_targetSlot) && view.ItemAt(_targetSlot) == _target) { StartDraw(view, now, _targetSlot); } else { LastResult = "Target item changed while storing"; } return; } } if (_phase == Phase.Drawing) { if (view.Held != _target || view.HeldBusy) { return; } _phase = Phase.Idle; } if (now > _queuedUntil) { _steps.Clear(); _putAway = false; _dropItem = 0; _directSlot = -1; } if (view.HeldBusy) { return; } EnsureReadySelection(view); if (_dropItem != 0) { int dropItem2 = _dropItem; _dropItem = 0; if (view.Held != dropItem2) { LastResult = "Held item changed; drop cancelled"; } else { LastResult = (view.DropHeld(dropItem2) ? "Dropped held item; removed from hotbar" : "Game rejected drop request"); } } else if (_putAway) { _putAway = false; if (_putAwayItem != 0 && view.Held != _putAwayItem) { LastResult = "Held item changed; storage cancelled"; } else if (view.Held == 0) { int num2 = SlotPolicy.Ready(view, _lastItem, _cursor); if (num2 >= 0) { StartDraw(view, now, num2); } else { LastResult = "No inventory item is ready yet"; } } else if (!view.CanStoreHeld) { LastResult = "Held object cannot go in inventory"; } else { int num3 = SlotPolicy.FirstFree(view); if (num3 < 0) { LastResult = "Inventory full; held item kept in hand"; } else { StartStore(view, now, num3, -1); } } } else if (_directSlot >= 0) { Direct(view, now); } else { if (_steps.Count == 0) { return; } int direction2 = _steps.Dequeue(); if (view.Held != 0) { if (!view.CanStoreHeld) { LastResult = "Held object cannot go in inventory"; _steps.Clear(); return; } int heldOrigin = view.HeldOrigin; int num4 = ((heldOrigin >= 0 && view.Available(heldOrigin) && view.ItemAt(heldOrigin) == 0) ? heldOrigin : SlotPolicy.FirstFree(view)); if (num4 < 0) { LastResult = "Inventory full; held item kept in hand"; _steps.Clear(); return; } int num5 = SlotPolicy.Next(view, num4, direction2, num4); if (num5 < 0 || num5 == num4) { LastResult = "No other occupied slot"; } else { StartStore(view, now, num4, num5); } } else { int num6 = SlotPolicy.Next(view, _cursor, direction2); if (num6 >= 0) { StartDraw(view, now, num6); } else { LastResult = "No occupied slots"; } } } } private void Direct(IInventoryView view, float now) { int directSlot = _directSlot; int num = view.ItemAt(directSlot); if (num != 0 && !view.StoredAndSettled(num)) { return; } _directSlot = -1; if (!view.Available(directSlot)) { LastResult = "Slot is no longer available"; return; } if (view.Held == 0) { if (num != 0) { StartDraw(view, now, directSlot); } return; } if (!view.CanStoreHeld) { LastResult = "Held object cannot go in inventory; kept in hand"; return; } if (num == 0) { StartStore(view, now, directSlot, -1); return; } int heldOrigin = view.HeldOrigin; int num2 = ((heldOrigin >= 0 && view.Available(heldOrigin) && view.ItemAt(heldOrigin) == 0) ? heldOrigin : SlotPolicy.FirstFree(view)); if (num2 < 0) { LastResult = "Inventory full; held item kept in hand"; } else { StartStore(view, now, num2, directSlot); } } private int RememberedSlot(IInventoryView view) { if (_lastItem != 0) { for (int i = 0; i < view.Count; i++) { if (view.Available(i) && view.ItemAt(i) == _lastItem) { return i; } } } return -1; } private void ObserveSelection(IInventoryView view) { if (view.Held != 0 && view.CanStoreHeld) { if (_lastItem != view.Held) { _lastItem = view.Held; _cursor = -1; } if (view.Available(view.HeldOrigin)) { _cursor = view.HeldOrigin; } } int num = RememberedSlot(view); if (num >= 0) { _cursor = num; } MainSlot = ((view.Available(_cursor) && (num >= 0 || (view.Held == _lastItem && view.CanStoreHeld))) ? _cursor : (-1)); } private void EnsureReadySelection(IInventoryView view) { if (_phase == Phase.Idle && view.Held == 0 && !view.HeldBusy) { int num = SlotPolicy.Ready(view, _lastItem, _cursor); if (num >= 0) { _lastItem = view.ItemAt(num); _cursor = num; } MainSlot = num; } } private void StartStore(IInventoryView view, float now, int slot, int next) { int held = view.Held; int target = ((next >= 0) ? view.ItemAt(next) : 0); if (!view.Store(slot)) { LastResult = "Game rejected storage request"; return; } _source = held; _targetSlot = next; _target = target; _cursor = slot; _phase = Phase.Storing; _deadline = now + Timeout; LastResult = "Putting held item away"; } private void StartDraw(IInventoryView view, float now, int slot) { int num = view.ItemAt(slot); if (num == 0 || !view.Draw(slot)) { LastResult = "Game rejected selection request"; return; } _target = num; _targetSlot = slot; _phase = Phase.Drawing; _deadline = now + Timeout; LastResult = "Selecting occupied slot " + (slot + 1); } } internal sealed class NativeInventory : IInventoryView { private readonly FieldRef<PhysGrabber, bool> _grabbed = AccessTools.FieldRefAccess<PhysGrabber, bool>("grabbed"); private readonly FieldRef<PhysGrabber, PhysGrabObject> _grabbedObject = AccessTools.FieldRefAccess<PhysGrabber, PhysGrabObject>("grabbedPhysGrabObject"); private readonly FieldRef<PhysGrabber, PhysGrabObject> _overrideObject = AccessTools.FieldRefAccess<PhysGrabber, PhysGrabObject>("overrideGrabTarget"); private readonly FieldRef<ItemEquippable, bool> _equipping = AccessTools.FieldRefAccess<ItemEquippable, bool>("isEquipping"); private readonly FieldRef<ItemEquippable, bool> _unequipping = AccessTools.FieldRefAccess<ItemEquippable, bool>("isUnequipping"); private readonly FieldRef<ItemEquippable, int> _origin = AccessTools.FieldRefAccess<ItemEquippable, int>("inventorySpotIndex"); private readonly FieldRef<PlayerAvatar, bool> _disabled = AccessTools.FieldRefAccess<PlayerAvatar, bool>("isDisabled"); private readonly FieldRef<PlayerController, float> _inputDisable = AccessTools.FieldRefAccess<PlayerController, float>("InputDisableTimer"); private readonly FieldRef<StatsManager, SortedDictionary<string, Dictionary<string, int>>> _stats = AccessTools.FieldRefAccess<StatsManager, SortedDictionary<string, Dictionary<string, int>>>("dictionaryOfDictionaries"); private readonly Func<ItemEquippable, int> _state = StateReader(); private readonly InventorySpot[] _spots = (InventorySpot[])(object)new InventorySpot[9]; private readonly Dictionary<int, int> _origins = new Dictionary<int, int>(); private Inventory _inventory; private PhysGrabber _grabber; private ItemEquippable _held; private PhysGrabObject _heldObject; private bool _hasHeldObject; private ConfigEntry<bool> _slrEnabled; private bool _slrDetected; private string _steamId; public object Identity => _inventory; public bool CanAct { get; private set; } public int Count { get; private set; } public int Held { get { if (!Object.op_Implicit((Object)(object)_held)) { if (!Object.op_Implicit((Object)(object)_heldObject)) { return 0; } return ((Object)_heldObject).GetInstanceID(); } return ((Object)_held).GetInstanceID(); } } public bool CanStoreHeld { get { if (Object.op_Implicit((Object)(object)_held)) { return !_inventory.IsItemEquipped(_held); } return false; } } public bool HeldBusy { get { if (!_hasHeldObject || _grabbed.Invoke(_grabber)) { if (Object.op_Implicit((Object)(object)_held)) { return Busy(_held); } return false; } return true; } } public int HeldOrigin { get { if (!Object.op_Implicit((Object)(object)_held) || !_origins.TryGetValue(((Object)_held).GetInstanceID(), out var value)) { return -1; } return value; } } public NativeInventory() { try { _slrDetected = Chainloader.PluginInfos.TryGetValue("SolarAaron.SLRUpgradePack", out var value) && (Object)(object)value.Instance != (Object)null; if (_slrDetected) { object obj = AccessTools.Property(((object)value.Instance).GetType(), "InventorySlotUpgradeInstance")?.GetValue(null); if (obj != null) { _slrEnabled = AccessTools.Property(obj.GetType(), "UpgradeEnabled")?.GetValue(obj) as ConfigEntry<bool>; } Plugin.Log.LogInfo((object)((_slrEnabled != null) ? "Optional SLR inventory support enabled automatically (0.6.0 layout)." : "SLR inventory API unavailable; standalone controls remain active for vanilla slots.")); } else { Plugin.Log.LogInfo((object)"SLRUpgradePack absent; standalone controls active for the three vanilla slots."); } } catch (Exception ex) { _slrEnabled = null; Plugin.Log.LogWarning((object)("Optional SLR support unavailable; standalone controls remain active. " + ex.GetBaseException().Message)); } } internal static bool GameplayAvailable() { if (!Application.isFocused || Time.timeScale <= 0f || (Object)(object)Inventory.instance == (Object)null || (Object)(object)PhysGrabber.instance == (Object)null || (Object)(object)PlayerController.instance == (Object)null || (Object)(object)InputManager.instance == (Object)null) { return false; } InputAction action = InputManager.instance.GetAction((InputKey)19); if (action != null && action.enabled && SemiFunc.NoTextInputsActive() && SemiFunc.NoExternalTextInputActive() && !SemiFunc.IsSpectating()) { return !SemiFunc.RunIsArena(); } return false; } public void Refresh() { Inventory instance = Inventory.instance; if ((Object)(object)_inventory != (Object)(object)instance) { _inventory = instance; _origins.Clear(); _steamId = null; Array.Clear(_spots, 0, _spots.Length); } _grabber = PhysGrabber.instance; _held = null; _heldObject = null; _hasHeldObject = false; CanAct = GameplayAvailable(); Count = 0; if ((Object)(object)_inventory == (Object)null || (Object)(object)_grabber == (Object)null || (Object)(object)PlayerController.instance == (Object)null) { return; } PlayerAvatar val = SemiFunc.PlayerAvatarLocal(); if ((Object)(object)val == (Object)null || _disabled.Invoke(val) || _inputDisable.Invoke(PlayerController.instance) > 0f) { CanAct = false; } if (_steamId == null && (Object)(object)val != (Object)null) { _steamId = SemiFunc.PlayerGetSteamID(val); } int value = 0; if (_slrEnabled != null && _slrEnabled.Value && (Object)(object)StatsManager.instance != (Object)null && _steamId != null) { SortedDictionary<string, Dictionary<string, int>> sortedDictionary = _stats.Invoke(StatsManager.instance); if (sortedDictionary != null && sortedDictionary.TryGetValue("playerUpgradeInventorySlot", out var value2)) { value2.TryGetValue(_steamId, out value); } } int num = InventoryCapacity.Limit(_slrDetected, _slrEnabled?.Value ?? false, value); Array.Clear(_spots, 0, _spots.Length); List<InventorySpot> allSpots = _inventory.GetAllSpots(); if (allSpots != null) { foreach (InventorySpot item in allSpots) { if ((Object)(object)item == (Object)null || !((Component)item).gameObject.activeInHierarchy) { continue; } int inventorySpotIndex = item.inventorySpotIndex; if (inventorySpotIndex >= 0 && inventorySpotIndex < num && inventorySpotIndex < _spots.Length) { _spots[inventorySpotIndex] = item; Count = Math.Max(Count, inventorySpotIndex + 1); if ((Object)(object)item.CurrentItem != (Object)null) { _origins[((Object)item.CurrentItem).GetInstanceID()] = inventorySpotIndex; } } } } PhysGrabObject val2 = (_grabbed.Invoke(_grabber) ? _grabbedObject.Invoke(_grabber) : _overrideObject.Invoke(_grabber)); if ((Object)(object)val2 != (Object)null) { _heldObject = val2; _hasHeldObject = true; _held = ((Component)val2).GetComponent<ItemEquippable>(); if ((Object)(object)_held != (Object)null && !_grabbed.Invoke(_grabber) && _inventory.IsItemEquipped(_held)) { _held = null; _heldObject = null; _hasHeldObject = false; } } } public bool Available(int slot) { if (slot >= 0 && slot < Count) { return (Object)(object)_spots[slot] != (Object)null; } return false; } internal InventorySpot SpotAt(int slot) { if (!Available(slot)) { return null; } return _spots[slot]; } public int ItemAt(int slot) { if (!Available(slot) || !((Object)(object)_spots[slot].CurrentItem != (Object)null)) { return 0; } return ((Object)_spots[slot].CurrentItem).GetInstanceID(); } public bool StoredAndSettled(int id) { for (int i = 0; i < Count; i++) { if (ItemAt(i) == id) { return !Busy(_spots[i].CurrentItem); } } return false; } private bool Busy(ItemEquippable item) { int num = _state(item); if (num != 1 && num != 3 && !_equipping.Invoke(item)) { return _unequipping.Invoke(item); } return true; } private static Func<ItemEquippable, int> StateReader() { FieldInfo fieldInfo = AccessTools.Field(typeof(ItemEquippable), "currentState"); if (fieldInfo == null || !fieldInfo.FieldType.IsEnum || Enum.GetUnderlyingType(fieldInfo.FieldType) != typeof(int)) { throw new MissingFieldException("ItemEquippable.currentState"); } DynamicMethod dynamicMethod = new DynamicMethod("SLRInventoryControls_ReadItemState", typeof(int), new Type[1] { typeof(ItemEquippable) }, typeof(ItemEquippable), skipVisibility: true); ILGenerator iLGenerator = dynamicMethod.GetILGenerator(); iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Ldfld, fieldInfo); iLGenerator.Emit(OpCodes.Ret); return (Func<ItemEquippable, int>)dynamicMethod.CreateDelegate(typeof(Func<ItemEquippable, int>)); } public bool Store(int slot) { if (!CanAct || !CanStoreHeld || HeldBusy || !Available(slot) || ItemAt(slot) != 0 || (Object)(object)_grabber.photonView == (Object)null) { return false; } _held.RequestEquip(slot, _grabber.photonView.ViewID); _origins[((Object)_held).GetInstanceID()] = slot; return true; } public bool Draw(int slot) { if (!CanAct || Held != 0 || !Available(slot) || ItemAt(slot) == 0 || Busy(_spots[slot].CurrentItem)) { return false; } _spots[slot].CurrentItem.RequestUnequip(); return true; } public bool DropHeld(int expectedItem) { if (!CanAct || expectedItem == 0 || Held != expectedItem || HeldBusy || !_grabbed.Invoke(_grabber)) { return false; } if ((Object)(object)_held != (Object)null && _inventory.IsItemEquipped(_held)) { return false; } _grabber.ReleaseObject(-1, 0.2f); _origins.Remove(expectedItem); return true; } internal void RememberSlot(ItemEquippable item) { if ((Object)(object)_inventory != (Object)null && (Object)(object)item != (Object)null && _inventory.IsItemEquipped(item)) { int num = _origin.Invoke(item); if (num >= 0 && num < 9) { _origins[((Object)item).GetInstanceID()] = num; } } } } [BepInPlugin("local.SLRInventoryControls", "Inventory Controls", "1.6.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [DefaultExecutionOrder(10000)] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "local.SLRInventoryControls"; public const string Name = "Inventory Controls"; public const string Version = "1.6.1"; internal static ManualLogSource Log; private static Plugin _instance; private ConfigEntry<bool> _enabled; private ConfigEntry<bool> _debug; private ConfigEntry<bool> _centerMain; private ConfigEntry<bool> _highlightMain; private ConfigEntry<bool> _keepHeld; private ConfigEntry<float> _timeout; private ConfigEntry<float> _holdSeconds; private ConfigEntry<float> _outlineThickness; private ConfigEntry<float> _outlinePadding; private ActionBinding _left; private ActionBinding _right; private ActionBinding _stow; private NativeInventory _inventory; private HotbarPresentation _hotbar; private ConfigEntry<float> _slotSpacing; private readonly InventoryCommands _commands = new InventoryCommands(); private readonly PutAwayGesture _gesture = new PutAwayGesture(); private BindingSample _stowSample; private object _inventoryIdentity; private int _inputFrame = -1; private int _direction; private bool _ready; private bool _claimsInput; private float _retryAt; private string _lastResult; private void Awake() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Expected O, but got Unknown //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Expected O, but got Unknown //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Expected O, but got Unknown //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Expected O, but got Unknown //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Expected O, but got Unknown //IL_02db: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Expected O, but got Unknown _instance = this; Log = ((BaseUnityPlugin)this).Logger; _enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable occupied-slot cycling, tap-to-store / re-equip, and hold-to-drop controls."); _timeout = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Switch timeout seconds", 2f, new ConfigDescription("Maximum wait for one inventory animation/network request. No item is dropped on timeout.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 5f), Array.Empty<object>())); _holdSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Hold to drop seconds", 0.4f, new ConfigDescription("Tap Put Away to store the held item in the first free slot, or re-equip the remembered / next ready hotbar item with empty hands. Hold this long to drop a held item instead. Mouse wheel always acts as a tap.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), Array.Empty<object>())); _keepHeld = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Never drop from slot keys", true, "Number and SLR slot keys swap items instead of dropping what you hold: it goes back to its slot, then the new item comes out. If that isn't possible, such as carrying a valuable or having a full inventory, what you hold stays in your hand. Letting go, holding Put Away, damage and physics still drop items as normal."); _debug = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Log actions", false, "Log command changes for testing. Keep off during normal play."); _centerMain = ((BaseUnityPlugin)this).Config.Bind<bool>("Hotbar", "Center main item", true, "Rotate displayed slot positions to keep the equipped / ready-to-re-equip item in the middle. Slot IDs, bindings and item ownership stay unchanged."); _highlightMain = ((BaseUnityPlugin)this).Config.Bind<bool>("Hotbar", "Highlight main item", true, "Draw a bright green border around the equipped / ready-to-re-equip slot."); _outlineThickness = ((BaseUnityPlugin)this).Config.Bind<float>("Hotbar", "Outline thickness", 4f, new ConfigDescription("Thickness of the bright green border in UI units.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>())); _slotSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("Hotbar", "Extra slot spacing", 8f, new ConfigDescription("Extra space between slots in UI units, so key labels clear the green border. The bar narrows itself if it would run off screen. 0 uses the game's spacing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 24f), Array.Empty<object>())); _outlinePadding = ((BaseUnityPlugin)this).Config.Bind<float>("Hotbar", "Outline padding", 4f, new ConfigDescription("Extra space around the selected slot's rectangle in UI units.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 12f), Array.Empty<object>())); _left = new ActionBinding(((BaseUnityPlugin)this).Config, "Cycle Left", (KeyCode)91, ControllerButton.DpadLeft); _right = new ActionBinding(((BaseUnityPlugin)this).Config, "Cycle Right", (KeyCode)93, ControllerButton.DpadRight); _stow = new ActionBinding(((BaseUnityPlugin)this).Config, "Put Away", (KeyCode)96, ControllerButton.DpadDown); try { _inventory = new NativeInventory(); Harmony val = new Harmony("local.SLRInventoryControls"); val.Patch((MethodBase)AccessTools.Method(typeof(InventorySpot), "HandleInput", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "HandleInputPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.Method(typeof(ItemEquippable), "RequestUnequip", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "RememberOriginPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _ready = true; try { _hotbar = new HotbarPresentation(); HarmonyMethod val2 = new HarmonyMethod(typeof(Plugin), "HotbarLayoutPrefix", (Type[])null) { priority = 800 }; string[] array = new string[3] { "LateUpdate", "LayoutSetup", "Start" }; foreach (string text in array) { val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(InventoryUI), text, Type.EmptyTypes, (Type[])null), val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } if (Chainloader.PluginInfos.TryGetValue("SolarAaron.SLRUpgradePack", out var value) && (Object)(object)value.Instance != (Object)null) { Type type = ((object)value.Instance).GetType().Assembly.GetType("SLRUpgradePack.UpgradeManagers.InventoryUIStartPatch"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.DeclaredMethod(type, "Postfix", (Type[])null, (Type[])null)); if (methodInfo != null) { val.Patch((MethodBase)methodInfo, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } } catch (Exception error) { DisableHotbar(error); } Log.LogInfo((object)"Ready. Standalone inventory controls active. Remap Cycle Left, Cycle Right and Put Away in REPOConfig / Inventory Controls. Optional SLR support activates automatically when detected."); } catch (Exception ex) { Log.LogError((object)("Inventory controls disabled: incompatible game/mod API. " + ex)); } } private void ReadInput() { if (_inputFrame != Time.frameCount) { _inputFrame = Time.frameCount; _stowSample = _stow.Sample(); bool flag = _left.Pressed(); bool flag2 = _right.Pressed(); bool flag3 = _stowSample.AnyPressed || _stowSample.AnyHeld || _gesture.Active; _claimsInput = flag3 || flag || flag2; _direction = ((!flag3 && flag != flag2) ? ((!flag) ? 1 : (-1)) : 0); } } private void LateUpdate() { if (!_ready || Time.unscaledTime < _retryAt) { UpdateHotbar(enabled: false); return; } try { ReadInput(); _inventory.Refresh(); _commands.Timeout = _timeout.Value; if (_inventoryIdentity != _inventory.Identity) { _gesture.Cancel(); _commands.Cancel(); _inventoryIdentity = _inventory.Identity; } if (!_enabled.Value || !_inventory.CanAct) { _gesture.Cancel(); _commands.Cancel(); } else { if (_stowSample.AnyPressed) { _commands.StopCycling(); } PutAwayDecision putAwayDecision = _gesture.Tick(_stowSample, Time.unscaledTime, _holdSeconds.Value, _inventory.Held, canAct: true); _commands.Tick(_inventory, Time.unscaledTime, _direction, putAwayDecision.Action == PutAwayAction.Store, (putAwayDecision.Action == PutAwayAction.Drop) ? putAwayDecision.Item : 0, (putAwayDecision.Action == PutAwayAction.Store) ? putAwayDecision.Item : 0); } if (_debug.Value && _commands.LastResult != _lastResult && _commands.LastResult != null) { _lastResult = _commands.LastResult; Log.LogInfo((object)_lastResult); } } catch (Exception ex) { _gesture.Cancel(); _commands.Cancel(); _retryAt = Time.unscaledTime + 5f; Log.LogWarning((object)("Inventory input paused for five seconds after an error: " + ex.GetBaseException().Message)); } UpdateHotbar(_enabled.Value && Time.unscaledTime >= _retryAt); } private void UpdateHotbar(bool enabled) { try { _hotbar?.Update(_inventory, _commands.MainSlot, enabled, _centerMain.Value, _highlightMain.Value, _outlineThickness.Value, _outlinePadding.Value, _slotSpacing.Value); } catch (Exception error) { DisableHotbar(error); } } private void DisableHotbar(Exception error) { try { _hotbar?.Dispose(); } catch { } _hotbar = null; Log.LogWarning((object)("Hotbar visuals disabled for this launch; inventory controls remain available: " + error.GetBaseException().Message)); } private static void HotbarLayoutPrefix() { try { _instance?._hotbar?.RestoreOffsets(); } catch (Exception error) { _instance?.DisableHotbar(error); } } private static bool HandleInputPrefix(InventorySpot __instance) { Plugin instance = _instance; if ((Object)(object)instance == (Object)null || !instance._ready || !instance._enabled.Value || Time.unscaledTime < instance._retryAt || !NativeInventory.GameplayAvailable()) { return true; } instance.ReadInput(); if (instance._claimsInput) { return false; } instance._gesture.Cancel(); if (instance._keepHeld.Value && instance._inventory.CanAct && instance._inventory.Available(__instance.inventorySpotIndex)) { instance._commands.SelectSlot(__instance.inventorySpotIndex, Time.unscaledTime); return false; } instance._commands.Cancel(); return true; } private static void RememberOriginPrefix(ItemEquippable __instance) { Plugin instance = _instance; if (instance != null && instance._ready) { _instance._inventory.RememberSlot(__instance); } } private void OnDestroy() { if ((Object)(object)_instance == (Object)(object)this) { _hotbar?.Dispose(); _gesture.Cancel(); _commands.Cancel(); _instance = null; } } } [Flags] internal enum BindingSource { None = 0, KeyboardMouse = 1, Controller = 2 } internal struct BindingSample { public BindingSource Pressed; public BindingSource Held; public BindingSource Connected; public bool WheelPulse; public bool AnyPressed { get { if (Pressed == BindingSource.None) { return WheelPulse; } return true; } } public bool AnyHeld => Held != BindingSource.None; } internal enum PutAwayAction { None, Store, Drop } internal struct PutAwayDecision { public PutAwayAction Action; public int Item; public PutAwayDecision(PutAwayAction action, int item) { Action = action; Item = item; } } internal sealed class PutAwayGesture { private bool _active; private bool _triggered; private BindingSource _sources; private float _started; private int _item; public bool Active => _active; public void Cancel() { _active = (_triggered = false); _sources = BindingSource.None; _item = 0; } public PutAwayDecision Tick(BindingSample input, float now, float holdSeconds, int heldItem, bool canAct) { if (!canAct) { Cancel(); return default(PutAwayDecision); } if (!_active) { if (input.Pressed == BindingSource.None) { if (input.WheelPulse) { return new PutAwayDecision(PutAwayAction.Store, heldItem); } return default(PutAwayDecision); } _active = true; _triggered = false; _sources = input.Pressed; _started = now; _item = heldItem; } if ((_sources & input.Connected) == 0) { Cancel(); return default(PutAwayDecision); } if (_item == 0 && heldItem != 0) { _item = heldItem; } else if (_item != 0 && heldItem != _item) { _triggered = true; } bool flag = (_sources & input.Held) != 0; if (!_triggered && now - _started >= Math.Max(0.1f, holdSeconds)) { _triggered = true; PutAwayDecision result = ((_item != 0) ? new PutAwayDecision(PutAwayAction.Drop, _item) : default(PutAwayDecision)); if (!flag) { Cancel(); } return result; } if (flag) { return default(PutAwayDecision); } PutAwayDecision result2 = ((!_triggered) ? new PutAwayDecision(PutAwayAction.Store, _item) : default(PutAwayDecision)); Cancel(); return result2; } } internal static class SlotPolicy { public static int FirstFree(IInventoryView view) { for (int i = 0; i < view.Count; i++) { if (view.Available(i) && view.ItemAt(i) == 0) { return i; } } return -1; } public static int Next(IInventoryView view, int anchor, int direction, int virtualOccupied = -1) { if (view.Count == 0 || direction == 0) { return -1; } int num = ((anchor >= 0 && anchor < view.Count) ? anchor : ((direction > 0) ? (view.Count - 1) : 0)); for (int i = 1; i <= view.Count; i++) { int num2 = (num + direction * i) % view.Count; if (num2 < 0) { num2 += view.Count; } if (view.Available(num2) && (view.ItemAt(num2) != 0 || num2 == virtualOccupied)) { return num2; } } return -1; } public static int Ready(IInventoryView view, int preferredItem, int anchor) { for (int i = 0; i < view.Count; i++) { if (view.Available(i) && view.ItemAt(i) != 0 && view.ItemAt(i) == preferredItem && view.StoredAndSettled(preferredItem)) { return i; } } if (view.Available(anchor) && view.ItemAt(anchor) != 0 && view.StoredAndSettled(view.ItemAt(anchor))) { return anchor; } int num = ((anchor >= 0 && anchor < view.Count) ? anchor : (view.Count - 1)); for (int j = 1; j <= view.Count; j++) { int num2 = (num + j) % view.Count; int num3 = (view.Available(num2) ? view.ItemAt(num2) : 0); if (num3 != 0 && view.StoredAndSettled(num3)) { return num2; } } return -1; } } internal interface IInventoryView { object Identity { get; } bool CanAct { get; } int Count { get; } int Held { get; } bool CanStoreHeld { get; } bool HeldBusy { get; } int HeldOrigin { get; } bool Available(int slot); int ItemAt(int slot); bool StoredAndSettled(int itemId); bool Draw(int slot); bool Store(int slot); bool DropHeld(int expectedItem); } }