diff --git a/GestureLock/GestureLock.csproj b/GestureLock/GestureLock.csproj
new file mode 100644
index 0000000..ae47bce
--- /dev/null
+++ b/GestureLock/GestureLock.csproj
@@ -0,0 +1,40 @@
+
+
+
+
+ net472
+ enable
+ latest
+ false
+
+
+
+
+ C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\MelonLoader\0Harmony.dll
+
+
+ C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll
+
+
+ C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\MelonLoader\MelonLoader.dll
+
+
+ ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\SteamVR.dll
+
+
+ ..\..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\ChilloutVR\Mods\UIExpansionKit.dll
+
+
+ C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll
+
+
+ C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.InputLegacyModule.dll
+
+
+
+
+
+
+
+
+
diff --git a/GestureLock/GestureLock.sln b/GestureLock/GestureLock.sln
new file mode 100644
index 0000000..65fa197
--- /dev/null
+++ b/GestureLock/GestureLock.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32630.192
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GestureLock", "GestureLock.csproj", "{B318B038-DA12-4960-A35E-613BE26B7965}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B318B038-DA12-4960-A35E-613BE26B7965}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B318B038-DA12-4960-A35E-613BE26B7965}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B318B038-DA12-4960-A35E-613BE26B7965}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B318B038-DA12-4960-A35E-613BE26B7965}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {0ABE9CFE-D4F5-4B5B-9D1A-5D0B1DA7E2CD}
+ EndGlobalSection
+EndGlobal
diff --git a/GestureLock/Main.cs b/GestureLock/Main.cs
new file mode 100644
index 0000000..ffc7af4
--- /dev/null
+++ b/GestureLock/Main.cs
@@ -0,0 +1,140 @@
+using ABI_RC.Core.UI;
+using ABI_RC.Core.Savior;
+using HarmonyLib;
+using MelonLoader;
+using UnityEngine;
+using Valve.VR;
+
+namespace GestureLock;
+
+public class GestureLock : MelonMod
+{
+ private static MelonPreferences_Category m_catagoryGestureLock;
+ private static MelonPreferences_Entry m_entryGestureLock;
+ private static MelonPreferences_Entry m_entryGestureBind;
+ private static MelonPreferences_Entry m_entryGestureHand;
+
+ private enum BindHand
+ {
+ LeftHand,
+ RightHand
+ }
+ private enum BindingOptions
+ {
+ ButtonATouch,
+ ButtonBTouch,
+ StickTouch,
+ TriggerTouch
+ }
+
+ public override void OnApplicationStart()
+ {
+ m_catagoryGestureLock = MelonPreferences.CreateCategory(nameof(GestureLock));
+ m_entryGestureLock = m_catagoryGestureLock.CreateEntry("Enabled", true, description: "Double-touch VR binding.");
+ m_entryGestureHand = m_catagoryGestureLock.CreateEntry("VR Hand", BindHand.LeftHand);
+ m_entryGestureBind = m_catagoryGestureLock.CreateEntry("VR Binding", BindingOptions.StickTouch);
+
+ m_catagoryGestureLock.SaveToFile(false);
+ m_entryGestureLock.OnValueChangedUntyped += UpdateSettings;
+ m_entryGestureHand.OnValueChangedUntyped += UpdateSettings;
+ m_entryGestureBind.OnValueChangedUntyped += UpdateSettings;
+
+ UpdateSettings();
+ }
+ private static void UpdateSettings()
+ {
+ HarmonyPatches.enabled = m_entryGestureLock.Value;
+ HarmonyPatches.bind = m_entryGestureBind.Value;
+ HarmonyPatches.hand = (SteamVR_Input_Sources)m_entryGestureHand.Value+1;
+ }
+
+ [HarmonyPatch]
+ private class HarmonyPatches
+ {
+ public static bool enabled = m_entryGestureLock.Value;
+ public static BindingOptions bind = m_entryGestureBind.Value;
+ public static SteamVR_Input_Sources hand = SteamVR_Input_Sources.LeftHand;
+
+ private static bool isLocked = false;
+ private static float oldGestureLeft = 0;
+ private static float oldGestureRight = 0;
+
+ private static bool toggleLock = false;
+ private static float touchDoubleTimer = 0f;
+ private static bool touchArmed = false;
+
+ private static void CheckTouch(bool input)
+ {
+ if (input)
+ {
+ if (touchArmed && touchDoubleTimer < 0.25f)
+ {
+ touchArmed = false;
+ toggleLock = true;
+ touchDoubleTimer = 1f;
+ }
+ else
+ {
+ touchDoubleTimer = 0f;
+ }
+ touchArmed = false;
+ }
+ else
+ {
+ touchArmed = true;
+ }
+ }
+
+ //Read VR Buttons
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(InputModuleSteamVR), "UpdateInput")]
+ private static void AfterUpdateInput(ref SteamVR_Action_Boolean ___steamVrButtonATouch, ref SteamVR_Action_Boolean ___steamVrButtonBTouch, ref SteamVR_Action_Boolean ___steamVrStickTouch, ref SteamVR_Action_Boolean ___steamVrTriggerTouch)
+ {
+ if (!MetaPort.Instance.isUsingVr || !enabled) return;
+
+ touchDoubleTimer += Time.deltaTime;
+ toggleLock = false;
+
+ switch (bind)
+ {
+ case BindingOptions.ButtonATouch:
+ CheckTouch(___steamVrButtonATouch.GetState(hand));
+ return;
+ case BindingOptions.ButtonBTouch:
+ CheckTouch(___steamVrButtonBTouch.GetState(hand));
+ return;
+ case BindingOptions.StickTouch:
+ CheckTouch(___steamVrStickTouch.GetState(hand));
+ return;
+ case BindingOptions.TriggerTouch:
+ CheckTouch(___steamVrTriggerTouch.GetState(hand));
+ return;
+ default: break;
+ }
+ }
+
+ //Apply GestureLock
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(CVRInputManager), "Update")]
+ private static void AfterUpdate(ref float ___gestureLeftRaw, ref float ___gestureLeft, ref float ___gestureRightRaw, ref float ___gestureRight)
+ {
+ if (!MetaPort.Instance.isUsingVr || !enabled) return;
+
+ if (toggleLock)
+ {
+ isLocked = !isLocked;
+ oldGestureLeft = ___gestureLeft;
+ oldGestureRight = ___gestureRight;
+ MelonLogger.Msg("Gestures " + (isLocked ? "Locked" : "Unlocked"));
+ CohtmlHud.Instance.ViewDropTextImmediate("", "Gesture Lock ", "Gestures " + (isLocked ? "Locked" : "Unlocked"));
+ }
+ if (isLocked)
+ {
+ ___gestureLeftRaw = oldGestureLeft;
+ ___gestureLeft = oldGestureLeft;
+ ___gestureRightRaw = oldGestureRight;
+ ___gestureRight = oldGestureRight;
+ }
+ }
+ }
+}
diff --git a/GestureLock/Properties/AssemblyInfo.cs b/GestureLock/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..94de873
--- /dev/null
+++ b/GestureLock/Properties/AssemblyInfo.cs
@@ -0,0 +1,30 @@
+using MelonLoader;
+using GestureLock.Properties;
+using System.Reflection;
+
+
+[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
+[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
+[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
+[assembly: AssemblyTitle(nameof(GestureLock))]
+[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
+[assembly: AssemblyProduct(nameof(GestureLock))]
+
+[assembly: MelonInfo(
+ typeof(GestureLock.GestureLock),
+ nameof(GestureLock),
+ AssemblyInfoParams.Version,
+ AssemblyInfoParams.Author,
+ downloadLink: ""
+)]
+
+[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
+[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
+[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
+
+namespace GestureLock.Properties;
+internal static class AssemblyInfoParams
+{
+ public const string Version = "1.0.0";
+ public const string Author = "NotAKidoS";
+}
\ No newline at end of file