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 UniversalGrasp v0.3.1
plugins\UniversalGrasp.dll
Decompiled a month 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.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("UniversalGrasp")] [assembly: AssemblyDescription("Hold any inventory item visually in either hand.")] [assembly: AssemblyCompany("bid")] [assembly: AssemblyProduct("UniversalGrasp")] [assembly: AssemblyCopyright("Copyright © 2021-2026 bid")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("0.3.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyVersion("0.3.1.0")] namespace UniversalGrasp { internal static class AttachSourceSelector { internal static GameObject Find(GameObject item) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown Transform obj = item.transform.Find("attach"); GameObject val = ((obj != null) ? ((Component)obj).gameObject : null); if ((Object)(object)val != (Object)null && val.GetComponentsInChildren<Renderer>(true).Any()) { return val; } GameObject val2 = null; bool flag = false; foreach (Transform item2 in item.transform) { Transform val3 = item2; if (((Component)val3).gameObject.layer == item.layer) { if ((Object)(object)val2 != (Object)null) { flag = true; break; } val2 = ((Component)val3).gameObject; } } if (!flag && (Object)(object)val2 != (Object)null) { return val2; } val2 = null; flag = false; foreach (Transform item3 in item.transform) { Transform val4 = item3; if ((Object)(object)val2 != (Object)null) { flag = true; break; } val2 = ((Component)val4).gameObject; } if (!flag && (Object)(object)val2 != (Object)null) { return val2; } return item; } } internal static class HandItemController { private static readonly FieldRef<Humanoid, ItemData> LeftItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_leftItem"); private static readonly FieldRef<Humanoid, ItemData> RightItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_rightItem"); private static readonly FieldRef<Humanoid, VisEquipment> VisualEquipment = AccessTools.FieldRefAccess<Humanoid, VisEquipment>("m_visEquipment"); private static readonly FieldRef<VisEquipment, int> VisualLeftItem = AccessTools.FieldRefAccess<VisEquipment, int>("m_leftItem"); private static readonly FieldRef<VisEquipment, int> VisualRightItem = AccessTools.FieldRefAccess<VisEquipment, int>("m_rightItem"); private static readonly FieldRef<Character, ZSyncAnimation> Animation = AccessTools.FieldRefAccess<Character, ZSyncAnimation>("m_zanim"); internal static bool IsSelectionActive() { if (UniversalGraspPlugin.Enabled != null && UniversalGraspPlugin.Enabled.Value) { return UniversalGraspPlugin.IsHandSelectionModifierPressed(); } return false; } internal static void SetHandItem(Player player, ItemData item, bool leftHand) { int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name); VisEquipment val = VisualEquipment.Invoke((Humanoid)(object)player); int num = CountItems(((Humanoid)player).GetInventory(), stableHashCode); if (leftHand) { ItemData val2 = LeftItem.Invoke((Humanoid)(object)player); if (val2 != null) { ((Humanoid)player).UnequipItem(val2, false); } int num2 = ((VisualLeftItem.Invoke(val) != stableHashCode) ? stableHashCode : 0); if (num2 != 0 && VisualRightItem.Invoke(val) == stableHashCode && num < 2) { ClearHand(player, val, leftHand: false); } int num3 = ((num2 != 0) ? item.m_quality : 0); val.SetLeftItem(num2, -1, num3); } else { ItemData val3 = RightItem.Invoke((Humanoid)(object)player); if (val3 != null) { ((Humanoid)player).UnequipItem(val3, false); } int num4 = ((VisualRightItem.Invoke(val) != stableHashCode) ? stableHashCode : 0); if (num4 != 0 && VisualLeftItem.Invoke(val) == stableHashCode && num < 2) { ClearHand(player, val, leftHand: true); } int num5 = ((num4 != 0) ? item.m_quality : 0); val.SetRightItem(num4, num5); } PlayEquipAnimation(player); } internal static void ReconcileWithInventory(Player player) { VisEquipment val = VisualEquipment.Invoke((Humanoid)(object)player); int num = ((LeftItem.Invoke((Humanoid)(object)player) == null) ? VisualLeftItem.Invoke(val) : 0); int num2 = ((RightItem.Invoke((Humanoid)(object)player) == null) ? VisualRightItem.Invoke(val) : 0); bool flag = false; if (num != 0 && num == num2) { int num3 = CountItems(((Humanoid)player).GetInventory(), num); if (num3 < 2) { ClearHand(player, val, leftHand: false); num2 = 0; flag = true; } if (num3 < 1) { ClearHand(player, val, leftHand: true); num = 0; flag = true; } } if (num != 0 && CountItems(((Humanoid)player).GetInventory(), num) == 0) { ClearHand(player, val, leftHand: true); flag = true; } if (num2 != 0 && CountItems(((Humanoid)player).GetInventory(), num2) == 0) { ClearHand(player, val, leftHand: false); flag = true; } if (flag) { PlayEquipAnimation(player); } } internal static bool ClearVisualOnlyItems(Player player) { VisEquipment val = VisualEquipment.Invoke((Humanoid)(object)player); bool result = false; if (RightItem.Invoke((Humanoid)(object)player) == null && VisualRightItem.Invoke(val) != 0) { val.SetRightItem(0, 0); result = true; } if (LeftItem.Invoke((Humanoid)(object)player) == null && VisualLeftItem.Invoke(val) != 0) { val.SetLeftItem(0, -1, 0); result = true; } return result; } internal static void PlayEquipAnimation(Player player) { Animation.Invoke((Character)(object)player).SetTrigger("equip_hip"); } private static int CountItems(Inventory inventory, int prefabHash) { int num = 0; foreach (ItemData allItem in inventory.GetAllItems()) { if ((Object)(object)allItem?.m_dropPrefab != (Object)null && StringExtensionMethods.GetStableHashCode(((Object)allItem.m_dropPrefab).name) == prefabHash) { num += allItem.m_stack; } } return num; } private static void ClearHand(Player player, VisEquipment visualEquipment, bool leftHand) { ItemData val = (leftHand ? LeftItem.Invoke((Humanoid)(object)player) : RightItem.Invoke((Humanoid)(object)player)); if (val != null) { ((Humanoid)player).UnequipItem(val, false); } if (leftHand) { visualEquipment.SetLeftItem(0, -1, 0); } else { visualEquipment.SetRightItem(0, 0); } } } [BepInPlugin("org.bepinex.plugins.bid.universalgrasp", "UniversalGrasp", "0.3.1")] internal sealed class UniversalGraspPlugin : BaseUnityPlugin { public const string PluginGuid = "org.bepinex.plugins.bid.universalgrasp"; public const string PluginName = "UniversalGrasp"; public const string PluginVersion = "0.3.1"; private readonly Harmony harmony = new Harmony("org.bepinex.plugins.bid.universalgrasp"); internal static ManualLogSource Log { get; private set; } internal static ConfigEntry<bool> Enabled { get; private set; } internal static ConfigEntry<KeyboardShortcut> HandSelectionModifier { get; private set; } private void Awake() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Client config", "enabled", true, "Enables modifier-click hand selection in the player inventory."); HandSelectionModifier = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Client config", "Hand selection modifier", new KeyboardShortcut((KeyCode)308, Array.Empty<KeyCode>()), "Key held while clicking an inventory item to put it into a hand."); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"UniversalGrasp 0.3.1 loaded."); } private void OnDestroy() { harmony.UnpatchSelf(); } internal static bool IsHandSelectionModifierPressed() { //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) if (HandSelectionModifier != null) { KeyboardShortcut value = HandSelectionModifier.Value; return ((KeyboardShortcut)(ref value)).IsPressed(); } return false; } private void Update() { Player localPlayer = Player.m_localPlayer; if (ZInput.instance != null && ZInput.GetButtonDown("Hide") && (Object)(object)localPlayer != (Object)null && HandItemController.ClearVisualOnlyItems(localPlayer)) { HandItemController.PlayEquipAnimation(localPlayer); } } } internal static class VisualCloneCleaner { internal sealed class PreparedSourceScope { private GameObject preparedSource; internal PreparedSourceScope Parent { get; } internal bool IsReleased { get; private set; } internal PreparedSourceScope(PreparedSourceScope parent) { Parent = parent; } internal void SetPreparedSource(GameObject source) { if (IsReleased) { throw new InvalidOperationException("Cannot prepare a source in a released scope."); } if ((Object)(object)preparedSource != (Object)null) { Object.DestroyImmediate((Object)(object)preparedSource); } preparedSource = source; } internal void Release() { if (!IsReleased) { IsReleased = true; if ((Object)(object)preparedSource != (Object)null) { Object.DestroyImmediate((Object)(object)preparedSource); preparedSource = null; } } } } private static readonly Dictionary<Type, List<Type>> ComponentDependencies = new Dictionary<Type, List<Type>>(); [ThreadStatic] private static PreparedSourceScope currentScope; internal static PreparedSourceScope BeginScope() { return currentScope = new PreparedSourceScope(currentScope); } internal static void ReleaseScope(PreparedSourceScope scope) { if (scope == null) { return; } scope.Release(); if (currentScope == scope) { currentScope = scope.Parent; while (currentScope != null && currentScope.IsReleased) { currentScope = currentScope.Parent; } } } internal static bool TryPrepareSource(GameObject source, out GameObject result, out string error) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0038: 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) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) result = null; error = null; PreparedSourceScope preparedSourceScope = currentScope; if (preparedSourceScope == null || preparedSourceScope.IsReleased) { error = "No active AttachItem prepared-source scope."; return false; } GameObject val = new GameObject("UniversalGrasp_VisualSource"); val.SetActive(false); try { Vector3 localScale = source.transform.localScale; result = InstantiateWithoutZNetViewInitialization(source, val.transform); if (!RemoveComponentsRecursively(result.transform, out error)) { result = null; return false; } ((Object)result).name = ((Object)source).name; result.transform.localPosition = Vector3.zero; result.transform.localRotation = Quaternion.identity; result.transform.localScale = localScale; result.SetActive(false); result.transform.SetParent((Transform)null, false); preparedSourceScope.SetPreparedSource(result); return true; } catch (Exception ex) { error = ex.Message; result = null; return false; } finally { Object.DestroyImmediate((Object)(object)val); } } private static GameObject InstantiateWithoutZNetViewInitialization(GameObject source, Transform parent) { bool forceDisableInit = ZNetView.m_forceDisableInit; ZNetView.m_forceDisableInit = true; try { return Object.Instantiate<GameObject>(source, parent); } finally { ZNetView.m_forceDisableInit = forceDisableInit; } } private static bool RemoveComponentsRecursively(Transform parent, out string error) { for (int i = 0; i < parent.childCount; i++) { if (!RemoveComponentsRecursively(parent.GetChild(i), out error)) { return false; } } List<Type> removalOrder = GetRemovalOrder(parent); if (removalOrder == null) { error = "Cyclic component dependencies detected on " + ((Object)parent).name + "."; return false; } foreach (Type item in removalOrder) { Component[] components = ((Component)parent).gameObject.GetComponents(item); foreach (Component val in components) { if (!IsVisualComponent(val)) { try { Object.DestroyImmediate((Object)(object)val); } catch (Exception ex) { error = "Could not remove " + item.FullName + " from " + ((Object)parent).name + ": " + ex.Message; return false; } } } } error = null; return true; } private static bool IsVisualComponent(Component component) { if (!(component is Transform) && !(component is MeshFilter) && !(component is Renderer) && !(component is Light) && !(component is ParticleSystem) && !(component is TrailRenderer) && !(component is LineRenderer)) { return component is Projector; } return true; } private static List<Type> GetRemovalOrder(Transform transform) { List<Type> list = new List<Type>(); HashSet<Type> visited = new HashSet<Type>(); HashSet<Type> recursionStack = new HashSet<Type>(); Component[] components = ((Component)transform).gameObject.GetComponents<Component>(); foreach (Component val in components) { if ((Object)(object)val != (Object)null && !TopologicalSort(((object)val).GetType(), visited, recursionStack, list)) { return null; } } list.Reverse(); return list; } private static bool TopologicalSort(Type type, HashSet<Type> visited, HashSet<Type> recursionStack, List<Type> result) { if (visited.Contains(type)) { return true; } List<Type> list = null; int num = result.FindIndex(type.IsSubclassOf); if (num >= 0) { list = result.Skip(num).ToList(); result.RemoveRange(num, result.Count - num); } if (!recursionStack.Add(type)) { return false; } foreach (Type dependency in GetDependencies(type)) { if (!TopologicalSort(dependency, visited, recursionStack, result)) { return false; } } if (list != null) { result.AddRange(list); } else { result.Add(type); } recursionStack.Remove(type); visited.Add(type); return true; } private static List<Type> GetDependencies(Type type) { if (ComponentDependencies.TryGetValue(type, out var value)) { return value; } value = new List<Type>(); foreach (RequireComponent customAttribute in ((MemberInfo)type).GetCustomAttributes<RequireComponent>(inherit: true)) { value.Add(customAttribute.m_Type0); value.Add(customAttribute.m_Type1); value.Add(customAttribute.m_Type2); } value = value.Where((Type dependency) => dependency != null).Distinct().ToList(); ComponentDependencies[type] = value; return value; } } } namespace UniversalGrasp.Patches { [HarmonyPatch(typeof(Humanoid), "DropItem")] internal static class HumanoidDropItemPatch { private static void Postfix(Humanoid __instance, Inventory inventory, bool __result) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (__result && (Object)(object)val != (Object)null && (Object)(object)val == (Object)(object)Player.m_localPlayer && inventory == ((Humanoid)val).GetInventory()) { HandItemController.ReconcileWithInventory(val); } } } internal static class InventoryGuiPatches { [HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")] private static class OnSelectedItemPatch { private static bool Prefix(InventoryGrid __0, ItemData __1) { return !TrySetHandItem(__0, __1, leftHand: true); } } [HarmonyPatch(typeof(InventoryGui), "OnRightClickItem")] private static class OnRightClickItemPatch { private static bool Prefix(InventoryGrid __0, ItemData __1) { return !TrySetHandItem(__0, __1, leftHand: false); } } private static bool TrySetHandItem(InventoryGrid grid, ItemData item, bool leftHand) { if (!HandItemController.IsSelectionActive()) { return false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)grid == (Object)null || item == null || (Object)(object)item.m_dropPrefab == (Object)null) { return false; } if (grid.GetInventory() != ((Humanoid)localPlayer).GetInventory()) { return false; } HandItemController.SetHandItem(localPlayer, item, leftHand); return true; } } [HarmonyPatch(typeof(VisEquipment), "AttachItem", new Type[] { typeof(int), typeof(int), typeof(Transform), typeof(bool), typeof(bool), typeof(int) })] internal static class VisEquipmentAttachItemPatch { private static readonly MethodInfo ObjectEqualityMethod = AccessTools.Method(typeof(Object), "op_Equality", new Type[2] { typeof(Object), typeof(Object) }, (Type[])null); private static readonly MethodInfo ChooseAttachSourceMethod = AccessTools.Method(typeof(VisEquipmentAttachItemPatch), "ChooseAttachSource", (Type[])null, (Type[])null); [HarmonyTranspiler] [HarmonyPriority(800)] private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = new List<CodeInstruction>(instructions); int num = FindAttachSelectionNullCheck(list); if (num < 0) { return list; } InjectAttachSelection(list, num); return list; } [HarmonyPostfix] private static void CorrectInvalidHandOffset(GameObject __result, Transform joint, VisualCloneCleaner.PreparedSourceScope __state) { //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_003a: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)__result != (Object)null && (Object)(object)joint != (Object)null && IsHandJoint(joint)) { Vector3 localPosition = __result.transform.localPosition; if (((Vector3)(ref localPosition)).sqrMagnitude > 100f) { __result.transform.localPosition = Vector3.zero; } } } finally { VisualCloneCleaner.ReleaseScope(__state); } } [HarmonyPrefix] private static void BeginPreparedSourceScope(out VisualCloneCleaner.PreparedSourceScope __state) { __state = VisualCloneCleaner.BeginScope(); } [HarmonyFinalizer] private static Exception RestoreInstantiationState(Exception __exception, VisualCloneCleaner.PreparedSourceScope __state) { VisualCloneCleaner.ReleaseScope(__state); return __exception; } private static int FindAttachSelectionNullCheck(IReadOnlyList<CodeInstruction> code) { int num = -1; for (int i = 0; i < code.Count; i++) { if (!IsLoadLocal(code[i], 1)) { continue; } int num2 = NextMeaningfulInstruction(code, i + 1); int num3 = NextMeaningfulInstruction(code, num2 + 1); int num4 = NextMeaningfulInstruction(code, num3 + 1); int num5 = NextMeaningfulInstruction(code, num4 + 1); int num6 = NextMeaningfulInstruction(code, num5 + 1); if (num2 >= 0 && num3 >= 0 && num4 >= 0 && num5 >= 0 && num6 >= 0 && !(code[num2].opcode != OpCodes.Ldnull) && CodeInstructionExtensions.Calls(code[num3], ObjectEqualityMethod) && code[num4].opcode.FlowControl == FlowControl.Cond_Branch && !(code[num5].opcode != OpCodes.Ldnull) && !(code[num6].opcode != OpCodes.Ret)) { if (num >= 0) { return -1; } num = i; } } return num; } private static void InjectAttachSelection(List<CodeInstruction> code, int selectionIndex) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown CodeInstruction val = new CodeInstruction(OpCodes.Ldloc_0, (object)null); MoveLabelsAndBlocks(code[selectionIndex], val); code.InsertRange(selectionIndex, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[6] { val, new CodeInstruction(OpCodes.Ldloc_1, (object)null), new CodeInstruction(OpCodes.Ldarg_3, (object)null), new CodeInstruction(OpCodes.Ldarg_S, (object)(byte)5), new CodeInstruction(OpCodes.Call, (object)ChooseAttachSourceMethod), new CodeInstruction(OpCodes.Stloc_1, (object)null) }); } private static int NextMeaningfulInstruction(IReadOnlyList<CodeInstruction> code, int startIndex) { for (int i = Math.Max(0, startIndex); i < code.Count; i++) { if (code[i].opcode != OpCodes.Nop) { return i; } } return -1; } private static bool IsLoadLocal(CodeInstruction instruction, int localIndex) { if (localIndex == 0 && instruction.opcode == OpCodes.Ldloc_0) { return true; } if (localIndex == 1 && instruction.opcode == OpCodes.Ldloc_1) { return true; } if (localIndex == 2 && instruction.opcode == OpCodes.Ldloc_2) { return true; } if (localIndex == 3 && instruction.opcode == OpCodes.Ldloc_3) { return true; } if (instruction.opcode != OpCodes.Ldloc && instruction.opcode != OpCodes.Ldloc_S) { return false; } if (instruction.operand is LocalBuilder localBuilder) { return localBuilder.LocalIndex == localIndex; } if (instruction.operand is LocalVariableInfo localVariableInfo) { return localVariableInfo.LocalIndex == localIndex; } if (!(instruction.operand is int num) || num != localIndex) { if (instruction.operand is byte b) { return b == localIndex; } return false; } return true; } private static void MoveLabelsAndBlocks(CodeInstruction source, CodeInstruction destination) { destination.labels.AddRange(source.labels); source.labels.Clear(); destination.blocks.AddRange(source.blocks); source.blocks.Clear(); } private static GameObject ChooseAttachSource(GameObject itemPrefab, GameObject vanillaSource, Transform joint, bool backAttach) { if ((Object)(object)itemPrefab == (Object)null || (Object)(object)joint == (Object)null || backAttach || ((Object)itemPrefab).name == "Lantern" || !IsHandJoint(joint)) { return vanillaSource; } if (IsWeapon(itemPrefab) && (Object)(object)vanillaSource != (Object)null) { return vanillaSource; } GameObject val = AttachSourceSelector.Find(itemPrefab) ?? vanillaSource; if ((Object)(object)val == (Object)null) { return null; } if (VisualCloneCleaner.TryPrepareSource(val, out var result, out var _)) { return result; } return val; } private static bool IsWeapon(GameObject itemPrefab) { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null && component.m_itemData != null) { return component.m_itemData.IsWeapon(); } return false; } private static bool IsHandJoint(Transform joint) { if (!(((Object)joint).name == "LeftHand_Attach")) { return ((Object)joint).name == "RightHand_Attach"; } return true; } } }