diff --git a/NomaiVR/ModConfig/IModSettingProvider.cs b/NomaiVR/ModConfig/IModSettingProvider.cs index 7058f18..36f37bb 100644 --- a/NomaiVR/ModConfig/IModSettingProvider.cs +++ b/NomaiVR/ModConfig/IModSettingProvider.cs @@ -17,6 +17,8 @@ namespace NomaiVR.ModConfig bool PreventClipping { get; } bool FlashlightGesture { get; } bool ControllerOrientedMovement { get; } + bool SnapTurning { get; } + string SnapTurnIncrement { get; } bool AutoHideToolbelt { get; } float ToolbeltHeight { get; } float HudScale { get; } diff --git a/NomaiVR/ModConfig/ModSettings.cs b/NomaiVR/ModConfig/ModSettings.cs index 3e51f2b..427c8e1 100644 --- a/NomaiVR/ModConfig/ModSettings.cs +++ b/NomaiVR/ModConfig/ModSettings.cs @@ -19,6 +19,8 @@ namespace NomaiVR.ModConfig public static bool PreventClipping => settingsProvider.PreventClipping; public static bool FlashlightGesture => settingsProvider.FlashlightGesture; public static bool ControllerOrientedMovement => settingsProvider.ControllerOrientedMovement; + public static bool SnapTurning => settingsProvider.SnapTurning; + public static string SnapTurnIncrement => settingsProvider.SnapTurnIncrement; public static bool AutoHideToolbelt => settingsProvider.AutoHideToolbelt; public static float ToolbeltHeight => settingsProvider.ToolbeltHeight; public static float HudScale => settingsProvider.HudScale; diff --git a/NomaiVR/ModConfig/OWMLSettingsProvider.cs b/NomaiVR/ModConfig/OWMLSettingsProvider.cs index 881a254..767681d 100644 --- a/NomaiVR/ModConfig/OWMLSettingsProvider.cs +++ b/NomaiVR/ModConfig/OWMLSettingsProvider.cs @@ -18,6 +18,8 @@ namespace NomaiVR.ModConfig public bool PreventClipping { get; private set; } public bool FlashlightGesture { get; private set; } public bool ControllerOrientedMovement { get; private set; } + public bool SnapTurning { get; private set; } + public string SnapTurnIncrement { get; private set; } public bool AutoHideToolbelt { get; private set; } public float ToolbeltHeight { get; private set; } public float HudScale { get; private set; } @@ -38,6 +40,8 @@ namespace NomaiVR.ModConfig VibrationStrength = config.GetSettingsValue("vibrationIntensity"); ShowHelmet = config.GetSettingsValue("helmetVisibility"); ControllerOrientedMovement = config.GetSettingsValue("movementControllerOriented"); + SnapTurning = config.GetSettingsValue("snapTurning"); + SnapTurnIncrement = config.GetSettingsValue("snapTurnIncrement"); EnableGesturePrompts = config.GetSettingsValue("showGesturePrompts"); EnableHandLaser = config.GetSettingsValue("showHandLaser"); EnableFeetMarker = config.GetSettingsValue("showFeetMarker"); diff --git a/NomaiVR/ModConfig/default-config.json b/NomaiVR/ModConfig/default-config.json index 17895ca..1a6df7f 100644 --- a/NomaiVR/ModConfig/default-config.json +++ b/NomaiVR/ModConfig/default-config.json @@ -78,6 +78,25 @@ "yes": "Controller", "no": "Head" }, + "snapTurning": { + "type": "toggle", + "value": false, + "title": "Snap turning", + "yes": "Enabled", + "no": "Disabled" + }, + "snapTurnIncrement": { + "type": "selector", + "value": "45", + "options": [ + "15", + "30", + "45", + "60", + "90" + ], + "title": "Snap turn increment" + }, "showGesturePrompts": { "type": "toggle", "value": true, diff --git a/NomaiVR/Player/PlayerBodyPosition.cs b/NomaiVR/Player/PlayerBodyPosition.cs index cff47c1..3eafc08 100644 --- a/NomaiVR/Player/PlayerBodyPosition.cs +++ b/NomaiVR/Player/PlayerBodyPosition.cs @@ -14,6 +14,8 @@ namespace NomaiVR.Player public class Behaviour : MonoBehaviour { + public static Action OnSnapTurn; + private Transform cameraParent; private static Transform playArea; private static OWCamera playerCamera; @@ -22,6 +24,10 @@ namespace NomaiVR.Player private static PlayerCharacterController playerController; private static Autopilot autopilot; private readonly SteamVR_Action_Boolean recenterAction = SteamVR_Actions._default.Recenter; + private static readonly SteamVR_Action_Vector2 turnAction = SteamVR_Actions._default.Look; + private static readonly float snapTurnInputThreshold = 0.15f; + private static bool isSnapTurnInCooldown = false; + private float lastTurnTime; internal void Start() { @@ -95,12 +101,23 @@ namespace NomaiVR.Player } UpdateRecenter(); + + if (ModSettings.SnapTurning) + { + // Check if time has passed and input stick was returned to neutral + if (isSnapTurnInCooldown && Time.time - lastTurnTime > 0.05f && Mathf.Abs(turnAction.axis.x) < snapTurnInputThreshold) + { + isSnapTurnInCooldown = false; + lastTurnTime = Time.time; + } + } } public class Patch : NomaiVRPatch { public override void ApplyPatches() { + Postfix(nameof(OWInput.GetAxisValue), nameof(PostGetAxisValue)); Postfix(nameof(PlayerCharacterController.UpdateTurning), nameof(PostCharacterTurning)); Postfix(nameof(JetpackThrusterController.FixedUpdate), nameof(PostThrusterUpdate)); Prefix("set_" + nameof(OWCamera.fieldOfView), nameof(PatchOwCameraFOV)); @@ -136,11 +153,25 @@ namespace NomaiVR.Player return; } + if (ModSettings.SnapTurning && !PlayerState.InZeroG()) + { + float turnInput = turnAction.axis.x; + + // If snap turning, only do the snap turn, skip reorienting the play area + if (!isSnapTurnInCooldown && Mathf.Abs(turnInput) > snapTurnInputThreshold) + { + isSnapTurnInCooldown = true; + float sign = Mathf.Sign(turnInput); + Quaternion snapRotation = Quaternion.AngleAxis(GetSnapTurnIncrement() * sign, playerBody.transform.up); + var fromToSnap = Quaternion.FromToRotation(playerBody.transform.forward, snapRotation * playerBody.transform.forward); + + rotationSetter(fromToSnap * playerBody.transform.rotation); + OnSnapTurn?.Invoke(); + return; + } + } var rotationSource = isControllerOriented ? LaserPointer.Behaviour.MovementLaser : playerCamera.transform; - - var fromTo = Quaternion.FromToRotation(playerBody.transform.forward, Vector3.ProjectOnPlane(rotationSource.transform.forward, playerBody.transform.up)); - var magnitude = 0f; if (!isControllerOriented) { @@ -154,6 +185,7 @@ namespace NomaiVR.Player } } + var fromTo = Quaternion.FromToRotation(playerBody.transform.forward, Vector3.ProjectOnPlane(rotationSource.transform.forward, playerBody.transform.up)); var targetRotation = fromTo * playerBody.transform.rotation; var inverseRotation = Quaternion.Inverse(fromTo) * playArea.rotation; @@ -170,6 +202,20 @@ namespace NomaiVR.Player } } + // Override vanilla input handling for disabling turning while snap turning is enabled + private static void PostGetAxisValue(ref Vector2 __result, IInputCommands command, InputMode mask) + { + if (!ModSettings.SnapTurning || (OWInput.GetInputMode() != InputMode.Character)) + { + return; + } + + if (command.CommandType == InputConsts.InputCommandType.LOOK) + { + __result = Vector2.zero; + } + } + private static bool PatchOwCameraFOV(OWCamera __instance) { //Prevents changing the fov of VR cameras @@ -183,6 +229,25 @@ namespace NomaiVR.Player if (__instance.mainCamera.stereoEnabled) __result = CameraHelper.GetScaledFieldOfView(__instance.mainCamera); return !__instance.mainCamera.stereoEnabled; } + + private static float GetSnapTurnIncrement() + { + switch(ModSettings.SnapTurnIncrement) + { + case "15": + return 15f; + case "30": + return 30f; + case "45": + return 45f; + case "60": + return 60f; + case "90": + return 90f; + default: + return 45f; + } + } } } diff --git a/NomaiVR/UI/HelmetFollowCameraRotation.cs b/NomaiVR/UI/HelmetFollowCameraRotation.cs index eae43c8..fe616dd 100644 --- a/NomaiVR/UI/HelmetFollowCameraRotation.cs +++ b/NomaiVR/UI/HelmetFollowCameraRotation.cs @@ -1,4 +1,5 @@ using NomaiVR.ModConfig; +using NomaiVR.Player; using UnityEngine; namespace NomaiVR.UI @@ -8,19 +9,22 @@ namespace NomaiVR.UI private Quaternion lastFrameRotation; private const float speed = 0.5f; private bool smoothEnabled = true; + private bool snapEnabled = false; + private bool snapTurnedLastFrame = false; private void Start() { lastFrameRotation = transform.rotation; - SetSmoothEnabled(); + RefreshEnabledSettings(); - ModSettings.OnConfigChange += SetSmoothEnabled; + ModSettings.OnConfigChange += RefreshEnabledSettings; } private void OnDestroy() { - ModSettings.OnConfigChange -= SetSmoothEnabled; + ModSettings.OnConfigChange -= RefreshEnabledSettings; + PlayerBodyPosition.Behaviour.OnSnapTurn -= OnSnapTurn; } private void LateUpdate() @@ -32,23 +36,41 @@ namespace NomaiVR.UI var targetRotation = Camera.main.transform.rotation; - if (smoothEnabled) + if (!smoothEnabled) + { + transform.rotation = targetRotation; + } + else if (snapEnabled && snapTurnedLastFrame) + { + snapTurnedLastFrame = false; + transform.rotation = targetRotation; + } + else { var difference = Mathf.Abs(Quaternion.Angle(lastFrameRotation, targetRotation)); var step = speed * Time.unscaledDeltaTime * difference * difference; transform.rotation = Quaternion.RotateTowards(lastFrameRotation, targetRotation, step); } - else - { - transform.rotation = targetRotation; - } lastFrameRotation = transform.rotation; } - private void SetSmoothEnabled() + private void RefreshEnabledSettings() { smoothEnabled = ModSettings.HudSmoothFollow; + snapEnabled = ModSettings.SnapTurning; + + PlayerBodyPosition.Behaviour.OnSnapTurn -= OnSnapTurn; + + if (snapEnabled) + { + PlayerBodyPosition.Behaviour.OnSnapTurn += OnSnapTurn; + } + } + + private void OnSnapTurn() + { + snapTurnedLastFrame = true; } } } \ No newline at end of file