From aecbe92a947ffd8a6c6aa64403e9f1637d74ac58 Mon Sep 17 00:00:00 2001 From: JaXt0r Date: Thu, 22 Oct 2026 07:04:00 +0200 Subject: [PATCH] refactor: VR game code uses own hand side, haptic and comfort setting types instead of HVR enums (HVR phase 0a). No player-visible change: Gothic.ini values keep their numbers. Co-Authored-By: Claude Opus 5.5 --- .../Scripts/GlobalEventDispatcher.cs | 8 ++- .../HVROverrides/VRPlayerController.cs | 42 ++++++----- .../Player/VRPlayerWeaponInteraction.cs | 5 +- .../VRContainerDoorPickingInteraction.cs | 16 ++--- .../Domain/Player/VrWeaponAttackDomain.cs | 18 ++--- Assets/Gothic-VR/Scripts/Interaction.meta | 8 +++ .../Scripts/Interaction/Backends.meta | 8 +++ .../Scripts/Interaction/Backends/Hvr.meta | 8 +++ .../Backends/Hvr/HvrEnumExtensions.cs | 69 +++++++++++++++++++ .../Backends/Hvr/HvrEnumExtensions.cs.meta | 2 + .../Gothic-VR/Scripts/Interaction/Models.meta | 8 +++ .../Scripts/Interaction/Models/VrButton.cs | 15 ++++ .../Interaction/Models/VrButton.cs.meta | 2 + .../Interaction/Models/VrButtonState.cs | 30 ++++++++ .../Interaction/Models/VrButtonState.cs.meta | 2 + .../Interaction/Models/VrGrabberKind.cs | 12 ++++ .../Interaction/Models/VrGrabberKind.cs.meta | 2 + .../Scripts/Interaction/Models/VrHandSide.cs | 14 ++++ .../Interaction/Models/VrHandSide.cs.meta | 2 + .../Models/VrHandSideExtensions.cs | 36 ++++++++++ .../Models/VrHandSideExtensions.cs.meta | 2 + .../Interaction/Models/VrHapticPreset.cs | 28 ++++++++ .../Interaction/Models/VrHapticPreset.cs.meta | 2 + .../Interaction/Models/VrMoveDirection.cs | 13 ++++ .../Models/VrMoveDirection.cs.meta | 2 + .../Scripts/Interaction/Models/VrSitStand.cs | 13 ++++ .../Interaction/Models/VrSitStand.cs.meta | 2 + .../Scripts/Interaction/Models/VrTurnType.cs | 12 ++++ .../Interaction/Models/VrTurnType.cs.meta | 2 + .../Context/VRContextInteractionService.cs | 4 +- .../Services/Context/VRContextMenuService.cs | 6 +- .../Scripts/Services/VRWeaponService.cs | 6 +- .../Scripts/Services/VrHapticsService.cs | 49 ++++++------- Docs/HVR-replacement/phase-0-decouple.md | 17 +++-- 34 files changed, 387 insertions(+), 78 deletions(-) create mode 100644 Assets/Gothic-VR/Scripts/Interaction.meta create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Backends.meta create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Backends/Hvr.meta create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Backends/Hvr/HvrEnumExtensions.cs create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Backends/Hvr/HvrEnumExtensions.cs.meta create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models.meta create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrButton.cs create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrButton.cs.meta create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrButtonState.cs create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrButtonState.cs.meta create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrGrabberKind.cs create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrGrabberKind.cs.meta create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSide.cs create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSide.cs.meta create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSideExtensions.cs create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSideExtensions.cs.meta create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrHapticPreset.cs create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrHapticPreset.cs.meta create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrMoveDirection.cs create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrMoveDirection.cs.meta create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrSitStand.cs create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrSitStand.cs.meta create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrTurnType.cs create mode 100644 Assets/Gothic-VR/Scripts/Interaction/Models/VrTurnType.cs.meta diff --git a/Assets/Gothic-Core/Scripts/GlobalEventDispatcher.cs b/Assets/Gothic-Core/Scripts/GlobalEventDispatcher.cs index aa7fefb6b..d8566a242 100644 --- a/Assets/Gothic-Core/Scripts/GlobalEventDispatcher.cs +++ b/Assets/Gothic-Core/Scripts/GlobalEventDispatcher.cs @@ -63,6 +63,11 @@ public static class GlobalEventDispatcher // Fight events + + /// + /// HINT: Left/Right have other values than Gothic.VR.Interaction.Models.VrHandSide (Left=0, Right=1). + /// Map them with VrHandSideExtensions, never cast. + /// public enum HandSide { None = 0, @@ -99,7 +104,8 @@ public enum HandSide // LockPicking events // 1. VobContainer -> LockPick // 2. VobContainer -> Door/Chest - // 3. HandSide (for VR) -> (0 = Left, 1 = Right) + // 3. HandSide (for VR) -> int of Gothic.VR.Interaction.Models.VrHandSide (0 = Left, 1 = Right), + // not GlobalEventDispatcher.HandSide. public static readonly UnityEvent LockPickComboCorrect = new(); public static readonly UnityEvent LockPickComboWrong = new(); public static readonly UnityEvent LockPickComboFinished = new(); diff --git a/Assets/Gothic-VR/Scripts/Adapters/HVROverrides/VRPlayerController.cs b/Assets/Gothic-VR/Scripts/Adapters/HVROverrides/VRPlayerController.cs index 12e790799..3a6e12e06 100644 --- a/Assets/Gothic-VR/Scripts/Adapters/HVROverrides/VRPlayerController.cs +++ b/Assets/Gothic-VR/Scripts/Adapters/HVROverrides/VRPlayerController.cs @@ -5,6 +5,8 @@ using Gothic.Core.Services; using Gothic.Core.Services.Config; using Gothic.VR.Adapters.UI; +using Gothic.VR.Interaction.Backends.Hvr; +using Gothic.VR.Interaction.Models; using HurricaneVR.Framework.Core.Player; using MyBox; using Reflex.Attributes; @@ -64,29 +66,35 @@ public void SetNormalControls(bool useDefaultValues = false) // We have our player created before Gothic inis are loaded. We therefore need to set some default values. if (useDefaultValues) { - CameraRig.SetSitStandMode(HVRSitStand.PlayerHeight); - DirectionStyle = PlayerDirectionMode.Camera; - RotationType = RotationType.Snap; - SnapAmount = 45f; - SmoothTurnSpeed = 90f; - + ApplyComfortSettings(VrSitStand.PlayerHeight, VrMoveDirection.Camera, VrTurnType.Snap, 45f, 90f); return; } - var sitStandSetting = - _configService.Gothic.GetInt(VRConstants.IniNames.SitStand, (int)HVRSitStand.PlayerHeight); - CameraRig.SetSitStandMode((HVRSitStand)sitStandSetting); - - DirectionStyle = (PlayerDirectionMode)_configService.Gothic.GetInt(VRConstants.IniNames.MoveDirection, (int)PlayerDirectionMode.Camera); - RotationType = (RotationType)_configService.Gothic.GetInt(VRConstants.IniNames.RotationType, (int)RotationType.Snap); + var gothicIni = _configService.Gothic; + var sitStand = (VrSitStand)gothicIni.GetInt(VRConstants.IniNames.SitStand, (int)VrSitStand.PlayerHeight); + var moveDirection = + (VrMoveDirection)gothicIni.GetInt(VRConstants.IniNames.MoveDirection, (int)VrMoveDirection.Camera); + var turnType = (VrTurnType)gothicIni.GetInt(VRConstants.IniNames.RotationType, (int)VrTurnType.Snap); - var snapSetting = _configService.Gothic.GetInt(VRConstants.IniNames.SnapRotationAmount, VRConstants.SnapRotationDefaultValue); + var snapSetting = gothicIni.GetInt(VRConstants.IniNames.SnapRotationAmount, VRConstants.SnapRotationDefaultValue); // e.g., 20° = 5° + 3*5° - SnapAmount = VRConstants.SnapRotationAmountSettingTickAmount + VRConstants.SnapRotationAmountSettingTickAmount * snapSetting; - - var smoothSetting = _configService.Gothic.GetFloat(VRConstants.IniNames.SmoothRotationSpeed, VRConstants.SmoothRotationDefaultValue); + var snapDegrees = VRConstants.SnapRotationAmountSettingTickAmount + VRConstants.SnapRotationAmountSettingTickAmount * snapSetting; + + var smoothSetting = gothicIni.GetFloat(VRConstants.IniNames.SmoothRotationSpeed, VRConstants.SmoothRotationDefaultValue); // e.g., 50 = 5 + 90 * 0.5f - SmoothTurnSpeed = VRConstants.SmoothRotationMinSpeed + VRConstants.SmoothRotationMaxAdditionalSpeed * smoothSetting; + var smoothSpeed = VRConstants.SmoothRotationMinSpeed + VRConstants.SmoothRotationMaxAdditionalSpeed * smoothSetting; + + ApplyComfortSettings(sitStand, moveDirection, turnType, snapDegrees, smoothSpeed); + } + + public void ApplyComfortSettings(VrSitStand sitStand, VrMoveDirection moveDirection, VrTurnType turnType, + float snapDegrees, float smoothSpeed) + { + CameraRig.SetSitStandMode(sitStand.ToHvr()); + DirectionStyle = moveDirection.ToHvr(); + RotationType = turnType.ToHvr(); + SnapAmount = snapDegrees; + SmoothTurnSpeed = smoothSpeed; } /// diff --git a/Assets/Gothic-VR/Scripts/Adapters/Player/VRPlayerWeaponInteraction.cs b/Assets/Gothic-VR/Scripts/Adapters/Player/VRPlayerWeaponInteraction.cs index 4bb3622e3..2ceb096ba 100644 --- a/Assets/Gothic-VR/Scripts/Adapters/Player/VRPlayerWeaponInteraction.cs +++ b/Assets/Gothic-VR/Scripts/Adapters/Player/VRPlayerWeaponInteraction.cs @@ -5,6 +5,7 @@ using Gothic.Core.Models.Marvin; using Gothic.Core.Models.Vm; using Gothic.Core.Services; +using Gothic.VR.Interaction.Backends.Hvr; using Gothic.VR.Models.Vob; using Gothic.VR.Services; using HurricaneVR.Framework.Core; @@ -56,7 +57,7 @@ public void OnGrabbed(HVRGrabberBase hand, HVRGrabbable item) if (itemInstance!.MainFlag != (int)VmGothicEnums.ItemFlags.ItemKatNf) return; - _weaponService.OnGrabbed(((HVRHandGrabber)hand).HandSide, vobContainer, GetWeaponPhysicsConfig()); + _weaponService.OnGrabbed(((HVRHandGrabber)hand).HandSide.ToVr(), vobContainer, GetWeaponPhysicsConfig()); SyncEquippedWeaponToHero(itemInstance, equip: true); } @@ -75,7 +76,7 @@ public void OnReleased(HVRGrabberBase hand, HVRGrabbable item) if (itemInstance!.MainFlag != (int)VmGothicEnums.ItemFlags.ItemKatNf) return; - _weaponService.OnReleased(((HVRHandGrabber)hand).HandSide, GetWeaponPhysicsConfig()); + _weaponService.OnReleased(((HVRHandGrabber)hand).HandSide.ToVr(), GetWeaponPhysicsConfig()); SyncEquippedWeaponToHero(itemInstance, equip: false); } diff --git a/Assets/Gothic-VR/Scripts/Adapters/Vob/LockPicking/VRContainerDoorPickingInteraction.cs b/Assets/Gothic-VR/Scripts/Adapters/Vob/LockPicking/VRContainerDoorPickingInteraction.cs index 7d7c94e22..a56cf5691 100644 --- a/Assets/Gothic-VR/Scripts/Adapters/Vob/LockPicking/VRContainerDoorPickingInteraction.cs +++ b/Assets/Gothic-VR/Scripts/Adapters/Vob/LockPicking/VRContainerDoorPickingInteraction.cs @@ -9,8 +9,8 @@ using Gothic.Core.Services.Vm; using Gothic.VR.Services; using Gothic.VR.Adapters.Vob.VobItem; +using Gothic.VR.Interaction.Models; using HurricaneVR.Framework.Components; -using HurricaneVR.Framework.Shared; using Reflex.Attributes; using UnityEngine; using ZenKit.Vobs; @@ -33,7 +33,7 @@ public class VRContainerDoorPickingInteraction : MonoBehaviour private bool _isLocked; private string _combination; private string _keyInstance; - private HVRHandSide _handSide; + private VrHandSide _handSide; private VobContainer _lockPick; private VobContainer _lockable; @@ -101,12 +101,12 @@ private void OnTriggerEnter(Collider other) if (IsHeldItem(_vrPlayerService.GrabbedItemLeft, other.gameObject)) { holdingHand = _vrPlayerService.GrabbedItemLeft.transform; - _handSide = HVRHandSide.Left; + _handSide = VrHandSide.Left; } else if (IsHeldItem(_vrPlayerService.GrabbedItemRight, other.gameObject)) { holdingHand = _vrPlayerService.GrabbedItemRight.transform; - _handSide = HVRHandSide.Right; + _handSide = VrHandSide.Right; } else { @@ -115,7 +115,7 @@ private void OnTriggerEnter(Collider other) _combinationPos = 0; PlaySound(_vmService.DoorLockSoundName); - _hapticsService.Vibrate(_handSide, VrHapticsService.VibrationType.Info); + _hapticsService.Vibrate(_handSide, VrHapticPreset.Info); // For later event usage. _lockPick = other.gameObject.GetComponentInParent().Container; @@ -213,13 +213,13 @@ private void TryUnlockWithKey(Collider other) return; // The player must actively hold the key (not just a dropped/flying item). - HVRHandSide hand; + VrHandSide hand; var leftRoot = _vrPlayerService.GrabbedItemLeft; var rightRoot = _vrPlayerService.GrabbedItemRight; if (leftRoot != null && leftRoot.GetComponentInParent() == vobLoader) - hand = HVRHandSide.Left; + hand = VrHandSide.Left; else if (rightRoot != null && rightRoot.GetComponentInParent() == vobLoader) - hand = HVRHandSide.Right; + hand = VrHandSide.Right; else return; diff --git a/Assets/Gothic-VR/Scripts/Domain/Player/VrWeaponAttackDomain.cs b/Assets/Gothic-VR/Scripts/Domain/Player/VrWeaponAttackDomain.cs index 926e79758..1b32e7341 100644 --- a/Assets/Gothic-VR/Scripts/Domain/Player/VrWeaponAttackDomain.cs +++ b/Assets/Gothic-VR/Scripts/Domain/Player/VrWeaponAttackDomain.cs @@ -13,9 +13,9 @@ using Gothic.Core.Models.Vm; using Gothic.Core.Services.Caches; using Gothic.VR.Models.Vob; +using Gothic.VR.Interaction.Models; using Gothic.VR.Services; using HurricaneVR.Framework.Core.Utils; -using HurricaneVR.Framework.Shared; using Reflex.Attributes; using UnityEngine; using ZenKit; @@ -87,7 +87,7 @@ private static readonly (float attackWindowTime, float comboWindowStart, float c /// FALSE, when: /// 1. Already handling one weapon, but another one is provided. /// - public bool TryHandle(VobContainer vobContainer, WeaponPhysicsConfig weaponConfig, HVRHandSide handSide, NpcContainer playerNpcContainer) + public bool TryHandle(VobContainer vobContainer, WeaponPhysicsConfig weaponConfig, VrHandSide handSide, NpcContainer playerNpcContainer) { if (!CanHandle(vobContainer)) return false; @@ -135,9 +135,9 @@ public bool TryHandle(VobContainer vobContainer, WeaponPhysicsConfig weaponConfi windowTimes.comboWindowStart, windowTimes.comboWindowTime); } - if (handSide == HVRHandSide.Left) + if (handSide == VrHandSide.Left) _handlesLeftHand = true; - else if (handSide == HVRHandSide.Right) + else if (handSide == VrHandSide.Right) _handlesRightHand = true; AlterWeaponWeights(weaponConfig); @@ -168,14 +168,14 @@ private bool CanHandle(VobContainer newWeapon) return false; } - public bool TryUnHandle(WeaponPhysicsConfig weaponConfig, HVRHandSide handSide) + public bool TryUnHandle(WeaponPhysicsConfig weaponConfig, VrHandSide handSide) { if (!CanUnHandle(handSide)) return false; - if (handSide == HVRHandSide.Left) + if (handSide == VrHandSide.Left) _handlesLeftHand = false; - else if (handSide == HVRHandSide.Right) + else if (handSide == VrHandSide.Right) _handlesRightHand = false; // Alter weight now, before we potentially unset VobContainer and Rigidbody. @@ -191,9 +191,9 @@ public bool TryUnHandle(WeaponPhysicsConfig weaponConfig, HVRHandSide handSide) /// Unhandle means: Do we released item in hand which we handled? /// Hint: We can only have one item in a hand at a time. Therefore, we don't need to check for the rigidbody itself. /// - private bool CanUnHandle(HVRHandSide releasedHandSide) + private bool CanUnHandle(VrHandSide releasedHandSide) { - if (releasedHandSide == HVRHandSide.Left) + if (releasedHandSide == VrHandSide.Left) return _handlesLeftHand; else return _handlesRightHand; diff --git a/Assets/Gothic-VR/Scripts/Interaction.meta b/Assets/Gothic-VR/Scripts/Interaction.meta new file mode 100644 index 000000000..a5ce655ce --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c17fb20ae6868424d94ea9dc7d7e3465 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Gothic-VR/Scripts/Interaction/Backends.meta b/Assets/Gothic-VR/Scripts/Interaction/Backends.meta new file mode 100644 index 000000000..92a4f1536 --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Backends.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2c7c846aa1d858e40893acb91583324c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Gothic-VR/Scripts/Interaction/Backends/Hvr.meta b/Assets/Gothic-VR/Scripts/Interaction/Backends/Hvr.meta new file mode 100644 index 000000000..8e0147cd7 --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Backends/Hvr.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0091983ca36e5ee43bf1d30f0b009682 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Gothic-VR/Scripts/Interaction/Backends/Hvr/HvrEnumExtensions.cs b/Assets/Gothic-VR/Scripts/Interaction/Backends/Hvr/HvrEnumExtensions.cs new file mode 100644 index 000000000..fa554aa4c --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Backends/Hvr/HvrEnumExtensions.cs @@ -0,0 +1,69 @@ +#if GOTHIC_HVR_INSTALLED +using System; +using Gothic.VR.Interaction.Models; +using HurricaneVR.Framework.Core.Player; +using HurricaneVR.Framework.Shared; + +namespace Gothic.VR.Interaction.Backends.Hvr +{ + /// + /// Maps our VR models to HVR types and back. Only used inside the HVR backend. + /// + public static class HvrEnumExtensions + { + public static HVRHandSide ToHvr(this VrHandSide side) + { + return side == VrHandSide.Left ? HVRHandSide.Left : HVRHandSide.Right; + } + + public static VrHandSide ToVr(this HVRHandSide side) + { + return side == HVRHandSide.Left ? VrHandSide.Left : VrHandSide.Right; + } + + public static HVRButtons ToHvr(this VrButton button) + { + return button switch + { + VrButton.Grip => HVRButtons.Grip, + VrButton.Trigger => HVRButtons.Trigger, + VrButton.Primary => HVRButtons.Primary, + VrButton.Secondary => HVRButtons.Secondary, + VrButton.Menu => HVRButtons.Menu, + VrButton.JoystickButton => HVRButtons.JoystickButton, + _ => throw new ArgumentOutOfRangeException(nameof(button), button, null) + }; + } + + public static VrButtonState ToVr(this HVRButtonState state) + { + return new VrButtonState(state.Active, state.JustActivated, state.JustDeactivated); + } + + public static HVRSitStand ToHvr(this VrSitStand sitStand) + { + return sitStand switch + { + VrSitStand.Sitting => HVRSitStand.Sitting, + VrSitStand.Standing => HVRSitStand.Standing, + _ => HVRSitStand.PlayerHeight + }; + } + + public static PlayerDirectionMode ToHvr(this VrMoveDirection direction) + { + return direction switch + { + VrMoveDirection.LeftController => PlayerDirectionMode.LeftController, + VrMoveDirection.RightController => PlayerDirectionMode.RightController, + _ => PlayerDirectionMode.Camera + }; + } + + public static RotationType ToHvr(this VrTurnType turnType) + { + return turnType == VrTurnType.Smooth ? RotationType.Smooth : RotationType.Snap; + } + } +} +#endif diff --git a/Assets/Gothic-VR/Scripts/Interaction/Backends/Hvr/HvrEnumExtensions.cs.meta b/Assets/Gothic-VR/Scripts/Interaction/Backends/Hvr/HvrEnumExtensions.cs.meta new file mode 100644 index 000000000..a54b1c782 --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Backends/Hvr/HvrEnumExtensions.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b30f5f87f90ba3b4690126ff005a8a5c \ No newline at end of file diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models.meta b/Assets/Gothic-VR/Scripts/Interaction/Models.meta new file mode 100644 index 000000000..ca85f8718 --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b880831f62bf2d447adb6325ed4ec0e7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrButton.cs b/Assets/Gothic-VR/Scripts/Interaction/Models/VrButton.cs new file mode 100644 index 000000000..7cf606c86 --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrButton.cs @@ -0,0 +1,15 @@ +namespace Gothic.VR.Interaction.Models +{ + /// + /// Controller buttons we read in game code. The active VR backend maps them to the device buttons. + /// + public enum VrButton + { + Grip, + Trigger, + Primary, + Secondary, + Menu, + JoystickButton + } +} diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrButton.cs.meta b/Assets/Gothic-VR/Scripts/Interaction/Models/VrButton.cs.meta new file mode 100644 index 000000000..96d07cc2c --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrButton.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8c026c8910e7f494bbb5de7a3557734d \ No newline at end of file diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrButtonState.cs b/Assets/Gothic-VR/Scripts/Interaction/Models/VrButtonState.cs new file mode 100644 index 000000000..738d661fd --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrButtonState.cs @@ -0,0 +1,30 @@ +namespace Gothic.VR.Interaction.Models +{ + /// + /// State of a controller button in the current frame. + /// + public readonly struct VrButtonState + { + /// + /// The button is held down. + /// + public readonly bool IsActive; + + /// + /// The button went down this frame. + /// + public readonly bool IsJustActivated; + + /// + /// The button went up this frame. + /// + public readonly bool IsJustDeactivated; + + public VrButtonState(bool isActive, bool isJustActivated, bool isJustDeactivated) + { + IsActive = isActive; + IsJustActivated = isJustActivated; + IsJustDeactivated = isJustDeactivated; + } + } +} diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrButtonState.cs.meta b/Assets/Gothic-VR/Scripts/Interaction/Models/VrButtonState.cs.meta new file mode 100644 index 000000000..3d2310c1b --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrButtonState.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c88e863b76ceab849857f40deb620293 \ No newline at end of file diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrGrabberKind.cs b/Assets/Gothic-VR/Scripts/Interaction/Models/VrGrabberKind.cs new file mode 100644 index 000000000..33aec975e --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrGrabberKind.cs @@ -0,0 +1,12 @@ +namespace Gothic.VR.Interaction.Models +{ + /// + /// What grabbed (or released) an interactable. + /// + public enum VrGrabberKind + { + Hand, + Distance, + Socket + } +} diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrGrabberKind.cs.meta b/Assets/Gothic-VR/Scripts/Interaction/Models/VrGrabberKind.cs.meta new file mode 100644 index 000000000..8b603377f --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrGrabberKind.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fafd204bff1d1d14191c07bfd3fb9b62 \ No newline at end of file diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSide.cs b/Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSide.cs new file mode 100644 index 000000000..b8b18c21e --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSide.cs @@ -0,0 +1,14 @@ +namespace Gothic.VR.Interaction.Models +{ + /// + /// A hand/controller of the VR player. + /// HINT: The lock picking events (GlobalEventDispatcher.LockPickCombo*) pass these values as int. + /// They differ from GlobalEventDispatcher.HandSide (None=0, Left=1, Right=2, Both=3). Use + /// VrHandSideExtensions to map between both. + /// + public enum VrHandSide + { + Left = 0, + Right = 1 + } +} diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSide.cs.meta b/Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSide.cs.meta new file mode 100644 index 000000000..3342049a2 --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSide.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 246dfc7049342c34392fdb668551b85c \ No newline at end of file diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSideExtensions.cs b/Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSideExtensions.cs new file mode 100644 index 000000000..685dd51b3 --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSideExtensions.cs @@ -0,0 +1,36 @@ +using Gothic.Core; + +namespace Gothic.VR.Interaction.Models +{ + /// + /// The only place where GlobalEventDispatcher.HandSide (fight events) and VrHandSide are mapped. + /// + public static class VrHandSideExtensions + { + /// + /// false for HandSide.None and HandSide.Both, as they have no single VR hand. + /// + public static bool TryToVrHandSide(this GlobalEventDispatcher.HandSide handSide, out VrHandSide vrHandSide) + { + switch (handSide) + { + case GlobalEventDispatcher.HandSide.Left: + vrHandSide = VrHandSide.Left; + return true; + case GlobalEventDispatcher.HandSide.Right: + vrHandSide = VrHandSide.Right; + return true; + default: + vrHandSide = default; + return false; + } + } + + public static GlobalEventDispatcher.HandSide ToHandSide(this VrHandSide vrHandSide) + { + return vrHandSide == VrHandSide.Left + ? GlobalEventDispatcher.HandSide.Left + : GlobalEventDispatcher.HandSide.Right; + } + } +} diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSideExtensions.cs.meta b/Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSideExtensions.cs.meta new file mode 100644 index 000000000..0ef669772 --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrHandSideExtensions.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 21fcabfad52958848a59f168ec14d816 \ No newline at end of file diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrHapticPreset.cs b/Assets/Gothic-VR/Scripts/Interaction/Models/VrHapticPreset.cs new file mode 100644 index 000000000..f9c2e8aba --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrHapticPreset.cs @@ -0,0 +1,28 @@ +namespace Gothic.VR.Interaction.Models +{ + /// + /// Vibration patterns. The VR backend defines amplitude, duration and frequency of each. + /// + public enum VrHapticPreset + { + /// + /// e.g., you are inside an interaction collider + /// + Info, + + /// + /// e.g., your lock pick rotation is invalid + /// + Warning, + + /// + /// e.g., your key is broken + /// + Error, + + /// + /// e.g., you opened a chest + /// + Success + } +} diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrHapticPreset.cs.meta b/Assets/Gothic-VR/Scripts/Interaction/Models/VrHapticPreset.cs.meta new file mode 100644 index 000000000..4ef1cfb86 --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrHapticPreset.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cd4d994077c26ea49a904516edc12339 \ No newline at end of file diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrMoveDirection.cs b/Assets/Gothic-VR/Scripts/Interaction/Models/VrMoveDirection.cs new file mode 100644 index 000000000..2eef267e2 --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrMoveDirection.cs @@ -0,0 +1,13 @@ +namespace Gothic.VR.Interaction.Models +{ + /// + /// Which device defines "forward" for thumbstick movement. + /// HINT: Stored as int in Gothic.ini (VRConstants.IniNames.MoveDirection). Never change the values. + /// + public enum VrMoveDirection + { + Camera = 0, + LeftController = 1, + RightController = 2 + } +} diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrMoveDirection.cs.meta b/Assets/Gothic-VR/Scripts/Interaction/Models/VrMoveDirection.cs.meta new file mode 100644 index 000000000..1f3279a37 --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrMoveDirection.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: aae46023bc545074093e3691112278b0 \ No newline at end of file diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrSitStand.cs b/Assets/Gothic-VR/Scripts/Interaction/Models/VrSitStand.cs new file mode 100644 index 000000000..96e6ec9dc --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrSitStand.cs @@ -0,0 +1,13 @@ +namespace Gothic.VR.Interaction.Models +{ + /// + /// How the camera height is calculated. + /// HINT: Stored as int in Gothic.ini (VRConstants.IniNames.SitStand). Never change the values. + /// + public enum VrSitStand + { + Sitting = 0, + Standing = 1, + PlayerHeight = 2 + } +} diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrSitStand.cs.meta b/Assets/Gothic-VR/Scripts/Interaction/Models/VrSitStand.cs.meta new file mode 100644 index 000000000..a79f7c1b7 --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrSitStand.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5d8c6e1091c15294294f29ae420a7520 \ No newline at end of file diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrTurnType.cs b/Assets/Gothic-VR/Scripts/Interaction/Models/VrTurnType.cs new file mode 100644 index 000000000..8b35fa5fe --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrTurnType.cs @@ -0,0 +1,12 @@ +namespace Gothic.VR.Interaction.Models +{ + /// + /// Thumbstick turning. + /// HINT: Stored as int in Gothic.ini (VRConstants.IniNames.RotationType). Never change the values. + /// + public enum VrTurnType + { + Smooth = 0, + Snap = 1 + } +} diff --git a/Assets/Gothic-VR/Scripts/Interaction/Models/VrTurnType.cs.meta b/Assets/Gothic-VR/Scripts/Interaction/Models/VrTurnType.cs.meta new file mode 100644 index 000000000..e816770a0 --- /dev/null +++ b/Assets/Gothic-VR/Scripts/Interaction/Models/VrTurnType.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a5df2400195474f4e8e30c2e737120cc \ No newline at end of file diff --git a/Assets/Gothic-VR/Scripts/Services/Context/VRContextInteractionService.cs b/Assets/Gothic-VR/Scripts/Services/Context/VRContextInteractionService.cs index a2473cca4..a84b90855 100644 --- a/Assets/Gothic-VR/Scripts/Services/Context/VRContextInteractionService.cs +++ b/Assets/Gothic-VR/Scripts/Services/Context/VRContextInteractionService.cs @@ -14,6 +14,8 @@ using Gothic.VR.Adapters.Marvin; using Gothic.VR.Adapters.Player; using Gothic.VR.Adapters.UI; +using Gothic.VR.Interaction.Backends.Hvr; +using Gothic.VR.Interaction.Models; using HurricaneVR.Framework.Core.UI; using HurricaneVRExtensions.Simulator; using Reflex.Attributes; @@ -133,7 +135,7 @@ private void OnMarvinLogoPoked(PointerEventData eventData) } ReflexProjectInstaller.DIContainer.Resolve() - .Vibrate(pointer.HandSide, VrHapticsService.VibrationType.Info); + .Vibrate(pointer.HandSide.ToVr(), VrHapticPreset.Info); } private void PlayerPrefsUpdated(string key, object value) diff --git a/Assets/Gothic-VR/Scripts/Services/Context/VRContextMenuService.cs b/Assets/Gothic-VR/Scripts/Services/Context/VRContextMenuService.cs index c1af0a34c..8f20d3690 100644 --- a/Assets/Gothic-VR/Scripts/Services/Context/VRContextMenuService.cs +++ b/Assets/Gothic-VR/Scripts/Services/Context/VRContextMenuService.cs @@ -9,7 +9,7 @@ using Gothic.Core; using Gothic.Core.Const; using Gothic.Core.Extensions; -using HurricaneVR.Framework.Core.Player; +using Gothic.VR.Interaction.Models; using Reflex.Attributes; using UnityEngine; using ZenKit.Daedalus; @@ -112,7 +112,7 @@ private AbstractMenuItemInstance CreateSitStandLabel(AbstractMenuInstance gameMe var sitStandSetting = new MutableMenuItemInstance("MENUITEM_GOTHIC_OPT_VR_ACCESSIBILITY_SIT_STAND_CHOICE", subtitlesChoice); sitStandSetting.PosY = posY; - sitStandSetting.SetUserFloat(3, (int)HVRSitStand.PlayerHeight); // Default value if no INI value exists. + sitStandSetting.SetUserFloat(3, (int)VrSitStand.PlayerHeight); // Default value if no INI value exists. sitStandSetting.SetText(0, _localizationService.GetText("menuitem.sitStand.value")); sitStandSetting.OnChgSetOption = VRConstants.IniNames.SitStand; @@ -167,7 +167,7 @@ private AbstractMenuItemInstance CreateRotationTypeChoicebox(AbstractMenuInstanc var rotationTypeSetting = new MutableMenuItemInstance("MENUITEM_GOTHIC_OPT_VR_ACCESSIBILITY_ROTATION_TYPE_CHOICE", subtitlesChoice); rotationTypeSetting.PosY = posY; - rotationTypeSetting.SetUserFloat(3, (int)RotationType.Snap); // Default value if no INI value exists. + rotationTypeSetting.SetUserFloat(3, (int)VrTurnType.Snap); // Default value if no INI value exists. rotationTypeSetting.SetText(0, _localizationService.GetText("menuitem.rotationType.value")); rotationTypeSetting.OnChgSetOption = VRConstants.IniNames.RotationType; diff --git a/Assets/Gothic-VR/Scripts/Services/VRWeaponService.cs b/Assets/Gothic-VR/Scripts/Services/VRWeaponService.cs index 38e9d54aa..f47a13de2 100644 --- a/Assets/Gothic-VR/Scripts/Services/VRWeaponService.cs +++ b/Assets/Gothic-VR/Scripts/Services/VRWeaponService.cs @@ -7,10 +7,10 @@ using Gothic.Core.Models.Vm; using Gothic.Core.Services.Player; using Gothic.VR.Domain.Player; +using Gothic.VR.Interaction.Models; using Gothic.VR.Models.Vob; using Gothic.Core.Extensions; using HurricaneVR.Framework.Core.Utils; -using HurricaneVR.Framework.Shared; using Reflex.Attributes; using UnityEngine; @@ -45,7 +45,7 @@ public void FixedUpdate() _secondAttackDomain.FixedUpdate(); } - public void OnGrabbed(HVRHandSide handSide, VobContainer vobContainer, WeaponPhysicsConfig weaponConfig) + public void OnGrabbed(VrHandSide handSide, VobContainer vobContainer, WeaponPhysicsConfig weaponConfig) { var heroContainer = _playerService.HeroContainer; @@ -56,7 +56,7 @@ public void OnGrabbed(HVRHandSide handSide, VobContainer vobContainer, WeaponPhy } } - public void OnReleased(HVRHandSide handSide, WeaponPhysicsConfig weaponConfig) + public void OnReleased(VrHandSide handSide, WeaponPhysicsConfig weaponConfig) { if (!_firstAttackDomain.TryUnHandle(weaponConfig, handSide)) { diff --git a/Assets/Gothic-VR/Scripts/Services/VrHapticsService.cs b/Assets/Gothic-VR/Scripts/Services/VrHapticsService.cs index d54dddb7b..3916460c3 100644 --- a/Assets/Gothic-VR/Scripts/Services/VrHapticsService.cs +++ b/Assets/Gothic-VR/Scripts/Services/VrHapticsService.cs @@ -1,6 +1,8 @@ #if GOTHIC_HVR_INSTALLED using Gothic.Core; using Gothic.Core.Models.Container; +using Gothic.VR.Interaction.Backends.Hvr; +using Gothic.VR.Interaction.Models; using HurricaneVR.Framework.Shared; using Reflex.Attributes; @@ -8,17 +10,10 @@ namespace Gothic.VR.Services { public class VrHapticsService { - public enum VibrationType - { - Info, - Warning, - Error, - Success - } - [Inject] private readonly VRPlayerService _vrPlayerService; [Inject] private readonly VRWeaponService _vrWeaponService; + // Indexed by VrHapticPreset. private readonly HapticData[] _vibrationData = { // Info - e.g., you are inside an interaction collider @@ -37,42 +32,38 @@ public enum VibrationType public VrHapticsService() { // Fight - GlobalEventDispatcher.FightWindowAttack.AddListener(combatant => AttackVibration(combatant, VibrationType.Success)); // Attack can happen now - GlobalEventDispatcher.FightWindowCombo.AddListener(combatant => AttackVibration(combatant, VibrationType.Info)); // Now you can start another attack - GlobalEventDispatcher.FightWindowComboFailed.AddListener(combatant => AttackVibration(combatant, VibrationType.Error)); // Attack window passed + GlobalEventDispatcher.FightWindowAttack.AddListener(combatant => AttackVibration(combatant, VrHapticPreset.Success)); // Attack can happen now + GlobalEventDispatcher.FightWindowCombo.AddListener(combatant => AttackVibration(combatant, VrHapticPreset.Info)); // Now you can start another attack + GlobalEventDispatcher.FightWindowComboFailed.AddListener(combatant => AttackVibration(combatant, VrHapticPreset.Error)); // Attack window passed // Lock picking - GlobalEventDispatcher.LockPickComboWrong.AddListener((_, _, handSide) => Vibrate((HVRHandSide)handSide, VibrationType.Warning)); - GlobalEventDispatcher.LockPickComboBroken.AddListener((_, _, handSide) => Vibrate((HVRHandSide)handSide, VibrationType.Error)); - GlobalEventDispatcher.LockPickComboCorrect.AddListener((_, _, handSide) => Vibrate((HVRHandSide)handSide, VibrationType.Success)); - GlobalEventDispatcher.LockPickComboFinished.AddListener((_, _, handSide) => Vibrate((HVRHandSide)handSide, VibrationType.Success)); + GlobalEventDispatcher.LockPickComboWrong.AddListener((_, _, handSide) => Vibrate((VrHandSide)handSide, VrHapticPreset.Warning)); + GlobalEventDispatcher.LockPickComboBroken.AddListener((_, _, handSide) => Vibrate((VrHandSide)handSide, VrHapticPreset.Error)); + GlobalEventDispatcher.LockPickComboCorrect.AddListener((_, _, handSide) => Vibrate((VrHandSide)handSide, VrHapticPreset.Success)); + GlobalEventDispatcher.LockPickComboFinished.AddListener((_, _, handSide) => Vibrate((VrHandSide)handSide, VrHapticPreset.Success)); } - private void AttackVibration(NpcContainer combatant, VibrationType vibrationType) + private void AttackVibration(NpcContainer combatant, VrHapticPreset preset) { // Resolve which hand holds the weapon for this combatant. // For NPCs attacking the player, we don't vibrate — only when it's the player's own attack. var handSide = _vrWeaponService.GetHandSideForCombatant(combatant); - switch (handSide) + if (handSide == GlobalEventDispatcher.HandSide.Both) + { + Vibrate(VrHandSide.Left, preset); + Vibrate(VrHandSide.Right, preset); + } + else if (handSide.TryToVrHandSide(out var vrHandSide)) { - case GlobalEventDispatcher.HandSide.Both: - Vibrate(HVRHandSide.Left, vibrationType); - Vibrate(HVRHandSide.Right, vibrationType); - break; - case GlobalEventDispatcher.HandSide.Left: - Vibrate(HVRHandSide.Left, vibrationType); - break; - case GlobalEventDispatcher.HandSide.Right: - Vibrate(HVRHandSide.Right, vibrationType); - break; + Vibrate(vrHandSide, preset); } } - public void Vibrate(HVRHandSide handSide, VibrationType type) + public void Vibrate(VrHandSide handSide, VrHapticPreset preset) { - _vrPlayerService.GetHand(handSide).Vibrate(_vibrationData[(int)type]); + _vrPlayerService.GetHand(handSide.ToHvr()).Vibrate(_vibrationData[(int)preset]); } } } diff --git a/Docs/HVR-replacement/phase-0-decouple.md b/Docs/HVR-replacement/phase-0-decouple.md index db972aea8..b698aa597 100644 --- a/Docs/HVR-replacement/phase-0-decouple.md +++ b/Docs/HVR-replacement/phase-0-decouple.md @@ -119,11 +119,15 @@ Split into four PRs so each one stays reviewable. ### PR 0a - Models and hand side -- [ ] Add `Interaction/Models/*`. -- [ ] Replace `HVRHandSide` in `VrWeaponAttackDomain`, `VRWeaponService`, `VRPlayerWeaponInteraction`, - `VRContainerDoorPickingInteraction`, `VrHapticsService`, `VRCinema`, `VRPlayerInputs`. -- [ ] `VRContextMenuService.cs:115` and `VRPlayerController.SetNormalControls`: replace `HVRSitStand`, - `PlayerDirectionMode` and `RotationType` with the new enums, converting at the HVR boundary. +- [x] Add `Interaction/Models/*` (`VrGrabArgs` waits for PR 0c, it needs `VrInteractable`). HVR mapping lives in + `Backends/Hvr/HvrEnumExtensions.cs` (`ToHvr()` / `ToVr()`). +- [x] Replace `HVRHandSide` in `VrWeaponAttackDomain`, `VRWeaponService`, `VRPlayerWeaponInteraction`, + `VRContainerDoorPickingInteraction`, `VrHapticsService` (`VibrationType` → `VrHapticPreset`). + `VRCinema` and `VRPlayerInputs` only pass it to `HVRController.GetButtonState`, so they move with `IVrInput` + in PR 0b. +- [x] `VRContextMenuService.cs:115` and `VRPlayerController.SetNormalControls`: replace `HVRSitStand`, + `PlayerDirectionMode` and `RotationType` with the new enums, converting at the HVR boundary + (`VRPlayerController.ApplyComfortSettings`, the later `IVrRig` method). ### PR 0b - Rig, input, haptics, UI ports @@ -142,6 +146,9 @@ Split into four PRs so each one stays reviewable. - [ ] `IVrUiPointers` ← `HvrUiPointers` (`HVRInputModule.UICanvases`, `AddCanvas`, `RemoveCanvas`, `Pointers`). Callers: `VRContextInteractionService.InitUIInteraction`, `OnMarvinLogoPoked`, `VirtualKeyboard`. - [ ] `VRSeat`: replace the `HVRCanvasFade` lookup with `IVrRig.Fade`. +- [ ] `SFXPlayer` (HVR's one-shot audio pool, not covered above): `VRWeaponService`, `VrWeaponAttackDomain`, + `VRSwimDive` play positional SFX through it. Needs an own replacement (e.g. via `AudioService`) - it's the + only reason these files still have `using HurricaneVR.Framework.Core.Utils`. ### PR 0c - Grab and socket events