Decompiled source of GoinGrabber v1.2.1

Mods/GoinGrabber.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using GoinGrabber;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppRUMBLE.Managers;
using Il2CppRUMBLE.Players.Subsystems;
using Il2CppRUMBLE.Utilities;
using MelonLoader;
using Microsoft.CodeAnalysis;
using RumbleModdingAPI;
using UnityEngine;
using UnityEngine.XR;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "GoinGrabber", "1.2.0", "Roaby", null)]
[assembly: MelonGame("Buckethead Entertainment", "RUMBLE")]
[assembly: MelonColor(50, 168, 164, 255)]
[assembly: MelonAuthorColor(50, 168, 164, 255)]
[assembly: VerifyLoaderVersion(0, 6, 6, true)]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("GoinGrabber")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+7ceffbc0a8cc109b0c18606887033e5315ca4186")]
[assembly: AssemblyProduct("GoinGrabber")]
[assembly: AssemblyTitle("GoinGrabber")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace GoinGrabber
{
	internal class BuildInfo
	{
		public const string ModName = "GoinGrabber";

		public const string Author = "Roaby";

		public const string ModVersion = "1.2.0";
	}
	public class Core : MelonMod
	{
		[HarmonyPatch(typeof(PlayerBoxInteractionSystem))]
		[HarmonyPatch("ExecuteFistBumpReward")]
		public static class FistBumpBonusPatch
		{
			[HarmonyPrefix]
			public static bool Prefix(PlayerBoxInteractionSystem __instance, Vector3 interactionPosition)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)__instance.onFistBumpBonusVFX != (Object)null)
				{
					AnimateGoinReplacer(interactionPosition);
					__instance.onFistBumpBonusVFX = null;
				}
				if ((Object)(object)__instance.onFistBumpBonusSFX != (Object)null)
				{
					__instance.onFistBumpBonusSFX = null;
				}
				return true;
			}
		}

		private string currentScene = "Loader";

		private bool lookingForNewObjects;

		private HashSet<int> knownInstanceIDs = new HashSet<int>();

		private static Vector3 gymGoinPosition = new Vector3(0f, 1.6f, 0f);

		public static Instance Logger { get; private set; }

		public override void OnInitializeMelon()
		{
			Logger = ((MelonBase)this).LoggerInstance;
			Logger.Msg("GoinGrabber: Initialized.");
			new GoinManager();
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			currentScene = sceneName;
			lookingForNewObjects = false;
			if (!ModResources.Initialized)
			{
				ModResources.LoadResources();
			}
			if (currentScene.Equals("Gym") || currentScene.Equals("Map0") || currentScene.Equals("Map1"))
			{
				MelonCoroutines.Start(CreateGoinInteractionObjects());
			}
		}

		public override void OnUpdate()
		{
		}

		private static void AnimateGoinReplacer(Vector3 position)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			GoinManager.Instance.CreateReplacementFistBumpGoin(position);
			ModResources.InstantiateFistbumpRing(position);
		}

		private IEnumerator CreateGoinInteractionObjects()
		{
			yield return (object)new WaitForSeconds(5f);
			Transform child = ((Component)Singleton<PlayerManager>.instance.localPlayer.Controller).transform.GetChild(0).GetChild(1).GetChild(0)
				.GetChild(4)
				.GetChild(0)
				.GetChild(2)
				.GetChild(0)
				.GetChild(0)
				.GetChild(0);
			Transform child2 = ((Component)Singleton<PlayerManager>.instance.localPlayer.Controller).transform.GetChild(0).GetChild(1).GetChild(0)
				.GetChild(4)
				.GetChild(0)
				.GetChild(1)
				.GetChild(0)
				.GetChild(0)
				.GetChild(0);
			ModResources.InstantiateHandSlapper(child, localPlayer: true, rightHand: true);
			ModResources.InstantiateHandSlapper(child2, localPlayer: true, rightHand: false);
			if (Singleton<PlayerManager>.instance.AllPlayers.Count > 1)
			{
				Transform child3 = ((Component)Singleton<PlayerManager>.instance.AllPlayers[1].Controller).transform.GetChild(0).GetChild(1).GetChild(0)
					.GetChild(4)
					.GetChild(0)
					.GetChild(2)
					.GetChild(0)
					.GetChild(0)
					.GetChild(0);
				Transform child4 = ((Component)Singleton<PlayerManager>.instance.AllPlayers[1].Controller).transform.GetChild(0).GetChild(1).GetChild(0)
					.GetChild(4)
					.GetChild(0)
					.GetChild(1)
					.GetChild(0)
					.GetChild(0)
					.GetChild(0);
				ModResources.InstantiateHandSlapper(child3, localPlayer: false, rightHand: true);
				ModResources.InstantiateHandSlapper(child4, localPlayer: false, rightHand: false);
			}
		}
	}
	[RegisterTypeInIl2Cpp]
	public class GoinHandler : MonoBehaviour
	{
		public float initialUpwardVelocity = 4f;

		public float tossGravity = -9.81f;

		public float spinSpeed = 500f;

		public AudioSource flipAudioSource;

		public GameObject catchPoint;

		public float hoverDuration = 0.5f;

		private float hoverTimer;

		private bool isHovering;

		private float verticalVelocity;

		private bool isFalling;

		private bool isLaunched;

		private Rigidbody rb;

		private bool caught;

		private Transform remotePlayerLeftHand;

		private Transform remotePlayerRightHand;

		private Transform localPlayerLeftHand;

		private Transform localPlayerRightHand;

		private Transform remotePlayerRightMiddleFinger;

		private Transform remotePlayerLeftMiddleFinger;

		private bool localRightHandTriggerPressedEarly;

		private bool localLeftHandTriggerPressedEarly;

		private bool remoteRightHandTriggerPressedEarly;

		private bool remoteLeftHandTriggerPressedEarly;

		private void Awake()
		{
			rb = ((Component)this).GetComponent<Rigidbody>();
			flipAudioSource = ((Component)this).gameObject.GetComponent<AudioSource>();
			if (Singleton<PlayerManager>.instance.AllPlayers.Count > 1)
			{
				remotePlayerRightHand = ((Component)Singleton<PlayerManager>.instance.AllPlayers[1].Controller).transform.GetChild(0).GetChild(1).GetChild(0)
					.GetChild(4)
					.GetChild(0)
					.GetChild(2)
					.GetChild(0)
					.GetChild(0)
					.GetChild(0);
				remotePlayerLeftHand = ((Component)Singleton<PlayerManager>.instance.AllPlayers[1].Controller).transform.GetChild(0).GetChild(1).GetChild(0)
					.GetChild(4)
					.GetChild(0)
					.GetChild(1)
					.GetChild(0)
					.GetChild(0)
					.GetChild(0);
				remotePlayerRightMiddleFinger = remotePlayerRightHand.GetChild(0).GetChild(0);
				remotePlayerLeftMiddleFinger = remotePlayerLeftHand.GetChild(0).GetChild(0);
			}
			localPlayerRightHand = ((Component)Singleton<PlayerManager>.instance.localPlayer.Controller).transform.GetChild(0).GetChild(1).GetChild(0)
				.GetChild(4)
				.GetChild(0)
				.GetChild(2)
				.GetChild(0)
				.GetChild(0)
				.GetChild(0);
			localPlayerLeftHand = ((Component)Singleton<PlayerManager>.instance.localPlayer.Controller).transform.GetChild(0).GetChild(1).GetChild(0)
				.GetChild(4)
				.GetChild(0)
				.GetChild(1)
				.GetChild(0)
				.GetChild(0)
				.GetChild(0);
		}

		private void Update()
		{
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_0060: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
			if (isLaunched)
			{
				if (!isFalling)
				{
					if (!isHovering)
					{
						verticalVelocity += tossGravity * Time.deltaTime;
						Transform transform = ((Component)this).transform;
						transform.position += Vector3.up * verticalVelocity * Time.deltaTime;
						((Component)this).transform.Rotate(Vector3.up, spinSpeed * Time.deltaTime);
						if (verticalVelocity <= 0f)
						{
							isHovering = true;
							hoverTimer = hoverDuration;
							verticalVelocity = 0f;
							rb.isKinematic = false;
							rb.angularVelocity = new Vector3(0f, 10f, 0f);
						}
					}
					else
					{
						hoverTimer -= Time.deltaTime;
						if (hoverTimer <= 0f)
						{
							isHovering = false;
							isFalling = true;
							verticalVelocity = 0f;
						}
					}
				}
				else
				{
					rb.useGravity = true;
					isLaunched = false;
				}
			}
			if (((Component)this).transform.position.y < -20f && !caught)
			{
				GoinManager.Instance.DestroyGoin(((Component)this).gameObject);
			}
		}

		private void OnTriggerEnter(Collider other)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			if (!rb.isKinematic)
			{
				if (((Object)other).name == "localRightCatcher" && (double)RightController.GetGrip() > 0.6)
				{
					localRightHandTriggerPressedEarly = true;
				}
				else if (((Object)other).name == "localLeftCatcher" && (double)LeftController.GetGrip() > 0.6)
				{
					localLeftHandTriggerPressedEarly = true;
				}
				else if (((Object)other).name == "remoteRightCatcher" && ((Component)remotePlayerRightMiddleFinger).transform.rotation.x > 70f && ((Component)remotePlayerRightMiddleFinger).transform.rotation.x < 90f)
				{
					remoteRightHandTriggerPressedEarly = true;
				}
				else if (((Object)other).name == "remoteLeftCatcher" && ((Component)remotePlayerLeftMiddleFinger).transform.rotation.x > 60f && ((Component)remotePlayerLeftMiddleFinger).transform.rotation.x < 80f)
				{
					remoteLeftHandTriggerPressedEarly = true;
				}
			}
		}

		private void OnTriggerStay(Collider other)
		{
			//IL_00b1: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			if (!rb.isKinematic)
			{
				if (((Object)other).name == "localRightCatcher" && (double)RightController.GetGrip() > 0.6 && !localRightHandTriggerPressedEarly)
				{
					VibrationUtil.Vibrate((XRNode)5);
					TriggerCatchAnimation();
				}
				else if (((Object)other).name == "localLeftCatcher" && (double)LeftController.GetGrip() > 0.6 && !localLeftHandTriggerPressedEarly)
				{
					VibrationUtil.Vibrate((XRNode)4);
					TriggerCatchAnimation();
				}
				else if (((Object)other).name == "remoteRightCatcher" && ((Component)remotePlayerRightMiddleFinger).transform.rotation.x > 70f && ((Component)remotePlayerRightMiddleFinger).transform.rotation.x < 90f && !remoteRightHandTriggerPressedEarly)
				{
					TriggerCatchAnimation();
				}
				else if (((Object)other).name == "remoteLeftCatcher" && ((Component)remotePlayerLeftMiddleFinger).transform.rotation.x > 60f && ((Component)remotePlayerLeftMiddleFinger).transform.rotation.x < 80f && !remoteLeftHandTriggerPressedEarly)
				{
					TriggerCatchAnimation();
				}
			}
		}

		private void OnTriggerExit(Collider other)
		{
			if (!rb.isKinematic)
			{
				if (((Object)other).name == "localRightCatcher")
				{
					localRightHandTriggerPressedEarly = false;
				}
				else if (((Object)other).name == "localLeftCatcher")
				{
					localLeftHandTriggerPressedEarly = false;
				}
				else if (((Object)other).name == "remoteRightCatcher")
				{
					remoteRightHandTriggerPressedEarly = false;
				}
				else if (((Object)other).name == "remoteLeftCatcher")
				{
					remoteLeftHandTriggerPressedEarly = false;
				}
			}
		}

		private void TriggerCatchAnimation()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			catchPoint = ModResources.InstantiateCatchPoint(((Component)this).transform.position);
			catchPoint.GetComponent<AudioSource>().Play();
			Object.Destroy((Object)(object)catchPoint, catchPoint.GetComponent<AudioSource>().clip.length);
			isLaunched = false;
			isFalling = false;
			GoinManager.Instance.DestroyGoin(((Component)this).gameObject);
			caught = true;
		}

		private void OnCollisionEnter(Collision collision)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			HandVelocityTracker handVelocityTracker = default(HandVelocityTracker);
			if (((Object)collision.gameObject).name.EndsWith("Slapper") && !caught && collision.gameObject.TryGetComponent<HandVelocityTracker>(ref handVelocityTracker))
			{
				hoverTimer = 0f;
				ContactPoint val = ((Il2CppArrayBase<ContactPoint>)(object)collision.contacts)[0];
				rb.AddForceAtPosition(handVelocityTracker.Velocity * handVelocityTracker.slapForce, ((ContactPoint)(ref val)).point, (ForceMode)1);
				if (((Object)collision.gameObject).name.StartsWith("localRight"))
				{
					VibrationUtil.Vibrate((XRNode)5, 0.8f, 0.3f);
				}
				else if (((Object)collision.gameObject).name.StartsWith("localLeft"))
				{
					VibrationUtil.Vibrate((XRNode)4, 0.8f, 0.3f);
				}
			}
		}

		public void Launch()
		{
			verticalVelocity = initialUpwardVelocity;
			isLaunched = true;
			isFalling = false;
			caught = false;
			flipAudioSource.Play();
			rb.isKinematic = true;
			rb.useGravity = false;
		}
	}
	public class GoinManager
	{
		public float initialTossUpwardVelocity = 3.5f;

		public float tossGravity = -15f;

		public float tossSpinSpeed = 500f;

		public float tossHoverDuration = 0.5f;

		public static GoinManager Instance { get; private set; }

		public List<GameObject> activeGoins { get; private set; } = new List<GameObject>();


		public int maxGoins { get; set; } = 10;


		public GoinManager()
		{
			Instance = this;
		}

		public void CreateReplacementFistBumpGoin(Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			GoinHandler component = AddGoinToScene(position).GetComponent<GoinHandler>();
			component.initialUpwardVelocity = initialTossUpwardVelocity;
			component.tossGravity = tossGravity;
			component.spinSpeed = tossSpinSpeed;
			component.hoverDuration = tossHoverDuration;
			component.Launch();
		}

		public GameObject AddGoinToScene(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = ModResources.InstantiateGoin(position);
			val.AddComponent<GoinHandler>();
			if (activeGoins.Count == 0)
			{
				EnableGoinPhysicsInteractions(enable: true);
			}
			activeGoins.Add(val);
			if (activeGoins.Count > maxGoins)
			{
				GameObject obj = activeGoins[0];
				activeGoins.RemoveAt(0);
				Object.Destroy((Object)(object)obj);
			}
			return val;
		}

		public void DestroyGoin(GameObject target)
		{
			if (activeGoins.Remove(target))
			{
				Object.Destroy((Object)(object)target);
			}
			if (activeGoins.Count == 0)
			{
				EnableGoinPhysicsInteractions(enable: false);
			}
		}

		public void ClearGoins()
		{
			activeGoins.Clear();
		}

		public void EnableGoinPhysicsInteractions(bool enable)
		{
			Physics.IgnoreLayerCollision(LayerMask.NameToLayer("Default"), LayerMask.NameToLayer("Default"), !enable);
			Physics.IgnoreLayerCollision(LayerMask.NameToLayer("Default"), LayerMask.NameToLayer("CombatFloor"), !enable);
			Physics.IgnoreLayerCollision(LayerMask.NameToLayer("Default"), LayerMask.NameToLayer("Floor"), !enable);
		}
	}
	[RegisterTypeInIl2Cpp]
	public class HandVelocityTracker : MonoBehaviour
	{
		public float slapForce = 0.5f;

		private Vector3 lastPosition;

		public Vector3 Velocity { get; private set; }

		private void Start()
		{
			//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)
			lastPosition = ((Component)this).transform.position;
		}

		private void Update()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			Velocity = (((Component)this).transform.position - lastPosition) / Time.deltaTime;
			lastPosition = ((Component)this).transform.position;
		}
	}
	internal class ModResources
	{
		public static Il2CppAssetBundle Bundle;

		public static GameObject GoinPrefab;

		private static bool initialized;

		public static bool Initialized => initialized;

		public static void LoadResources(bool reload = false)
		{
			if (!initialized || reload)
			{
				Bundle = Il2CppAssetBundleManager.LoadFromFile("UserData/goingrabberbundle");
				initialized = true;
			}
		}

		public static GameObject InstantiateGoin(Vector3 position)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = Object.Instantiate<GameObject>(Bundle.LoadAsset<GameObject>("Goin"));
			obj.transform.position = position;
			((Object)obj).name = "CustomGoin";
			return obj;
		}

		public static GameObject InstantiateCatchPoint(Vector3 position)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = Object.Instantiate<GameObject>(Bundle.LoadAsset<GameObject>("CatchPoint"));
			obj.transform.position = position;
			((Object)obj).name = "CatchPoint";
			return obj;
		}

		public static GameObject InstantiateFistbumpRing(Vector3 position)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = Object.Instantiate<GameObject>(Bundle.LoadAsset<GameObject>("FistbumpRing"));
			obj.transform.position = position;
			obj.AddComponent<RingDissolver>();
			((Object)obj).name = "FistbumpRing";
			return obj;
		}

		public static GameObject InstantiateHandSlapper(Transform parent, bool localPlayer, bool rightHand)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(Bundle.LoadAsset<GameObject>("HandSlapper"), parent);
			((Object)val).name = "HandSlapper";
			Transform val2 = val.transform.Find("GoinCatcher");
			if ((Object)(object)val2 != (Object)null)
			{
				string text = (localPlayer ? "local" : "remote");
				text += (rightHand ? "RightCatcher" : "LeftCatcher");
				((Object)val2).name = text;
			}
			if (!rightHand)
			{
				val.transform.localRotation = Quaternion.Euler(0f, 165f, 15f);
				Vector3 localPosition = val.transform.localPosition;
				localPosition.x = 0f - localPosition.x;
				val.transform.localPosition = localPosition;
			}
			Transform val3 = val.transform.Find("SlapCollider");
			if ((Object)(object)val3 != (Object)null)
			{
				((Component)val3).gameObject.AddComponent<HandVelocityTracker>();
				string text2 = (localPlayer ? "local" : "remote");
				text2 += (rightHand ? "RightSlapper" : "LeftSlapper");
				((Object)val3).name = text2;
			}
			((Component)val.transform.Find("VisualCapsule")).gameObject.SetActive(false);
			((Component)val.transform.Find("VisualHand")).gameObject.SetActive(false);
			return val;
		}
	}
	[RegisterTypeInIl2Cpp]
	internal class RingDissolver : MonoBehaviour
	{
		public Material material;

		public float dissolveTime = 1f;

		private float startTime;

		private void Start()
		{
			material = ((Component)this).GetComponent<Renderer>().material;
			startTime = Time.time;
		}

		private void Update()
		{
			float num = Time.time - startTime;
			float num2 = Mathf.Clamp01(num / dissolveTime);
			float num3 = Mathf.Lerp(0.1f, 1f, num2);
			material.SetFloat("_DissolveAmount", num3);
			if (num >= dissolveTime)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
		}
	}
	public static class VibrationUtil
	{
		public static void Vibrate(XRNode node, float amplitude = 0.5f, float duration = 0.2f)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: 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)
			try
			{
				InputDevice deviceAtXRNode = InputDevices.GetDeviceAtXRNode(node);
				((InputDevice)(ref deviceAtXRNode)).SendHapticImpulse(0u, amplitude, duration);
			}
			catch (Exception ex)
			{
				Core.Logger.Error("Failed to send haptic feedback: " + ex.Message);
			}
		}
	}
}