diff --git a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel.csproj b/GrabbableSteeringWheel/GrabbableSteeringWheel.csproj
similarity index 86%
rename from RCCVirtualSteeringWheel/RCCVirtualSteeringWheel.csproj
rename to GrabbableSteeringWheel/GrabbableSteeringWheel.csproj
index 8920c39..9855f81 100644
--- a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel.csproj
+++ b/GrabbableSteeringWheel/GrabbableSteeringWheel.csproj
@@ -2,7 +2,6 @@
net48
- GrabbableSteeringWheel
@@ -12,4 +11,7 @@
..\.ManagedLibs\TheClapper.dll
+
+
+
diff --git a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Util/BoneVertexBoundsUtility.cs b/GrabbableSteeringWheel/GrabbableSteeringWheel/BoneVertexBoundsUtility.cs
similarity index 99%
rename from RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Util/BoneVertexBoundsUtility.cs
rename to GrabbableSteeringWheel/GrabbableSteeringWheel/BoneVertexBoundsUtility.cs
index 093620c..2864f49 100644
--- a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Util/BoneVertexBoundsUtility.cs
+++ b/GrabbableSteeringWheel/GrabbableSteeringWheel/BoneVertexBoundsUtility.cs
@@ -6,8 +6,6 @@ using Unity.Profiling;
using UnityEngine;
using UnityEngine.Animations;
-namespace NAK.RCCVirtualSteeringWheel.Util;
-
public class BoneVertexBoundsUtility : MonoBehaviour
{
private static readonly ProfilerMarker s_calculateBoundsMarker = new("BoneVertexBounds.Calculate");
@@ -203,7 +201,7 @@ public class BoneVertexBoundsUtility : MonoBehaviour
{
yield return StartCoroutine(ProcessSkinnedMeshRenderersLocal(bone, weightThreshold, skinnedPoints =>
{
- if (skinnedPoints is not { Count: > 0 }) return;
+ if (skinnedPoints == null || skinnedPoints.Count <= 0) return;
allWeightedPoints.AddRange(skinnedPoints);
hasValidPoints = true;
}));
diff --git a/GrabbableSteeringWheel/GrabbableSteeringWheel/SteeringWheelPickup.cs b/GrabbableSteeringWheel/GrabbableSteeringWheel/SteeringWheelPickup.cs
new file mode 100644
index 0000000..0807a2a
--- /dev/null
+++ b/GrabbableSteeringWheel/GrabbableSteeringWheel/SteeringWheelPickup.cs
@@ -0,0 +1,203 @@
+using ABI_RC.Core.InteractionSystem;
+using ABI_RC.Core.InteractionSystem.Base;
+using UnityEngine;
+
+// TODO:
+// Fix multi-grab (limitation of Pickupable)
+// Think this can be fixed by forcing ungrab & monitoring ourselves for release
+// Add configurable override for steering range
+// Fix steering wheel resetting immediatly on release
+// Fix input patch not being multiplicative (so joysticks can still work)
+// Prevent pickup in Desktop
+
+public class SteeringWheelPickup : Pickupable
+{
+ private RCC_CarControllerV3 _carController;
+
+ private static readonly Dictionary ActiveWheels = new();
+
+ public static float GetSteerInput(RCC_CarControllerV3 carController)
+ {
+ if (ActiveWheels.TryGetValue(carController, out SteeringWheelPickup wheel) && wheel.IsPickedUp)
+ return wheel.GetNormalizedValue();
+ return 0f;
+ }
+
+ public void SetupSteeringWheel(RCC_CarControllerV3 carController)
+ {
+ _carController = carController;
+ if (!ActiveWheels.ContainsKey(carController))
+ {
+ ActiveWheels[carController] = this;
+ carController.useCounterSteering = false;
+ carController.useSteeringSmoother = false;
+ }
+ }
+
+ private void OnDestroy()
+ {
+ if (_carController != null && ActiveWheels.ContainsKey(_carController))
+ ActiveWheels.Remove(_carController);
+ }
+
+ #region Configuration Properties Override
+
+ public override bool DisallowTheft => true;
+ public override float MaxGrabDistance => 0.8f;
+ public override float MaxPushDistance => 0f;
+ public override bool IsAutoHold => false;
+ public override bool IsObjectRotationAllowed => false;
+ public override bool IsObjectPushPullAllowed => false;
+ public override bool IsObjectUseAllowed => false;
+
+ public override bool CanPickup => IsPickupable && _carController?.SteeringWheel != null;
+
+ #endregion Configuration Properties Override
+
+ #region RCC Stuff
+
+ private float GetMaxSteeringRange()
+ => _carController.steerAngle * Mathf.Abs(_carController.steeringWheelAngleMultiplier);
+
+ private float GetSteeringWheelSign()
+ => Mathf.Sign(_carController.steeringWheelAngleMultiplier) * -1f; // Idk
+
+ private Vector3 GetSteeringWheelLocalAxis()
+ {
+ return _carController.steeringWheelRotateAround switch
+ {
+ RCC_CarControllerV3.SteeringWheelRotateAround.XAxis => Vector3.right,
+ RCC_CarControllerV3.SteeringWheelRotateAround.YAxis => Vector3.up,
+ RCC_CarControllerV3.SteeringWheelRotateAround.ZAxis => Vector3.forward,
+ _ => Vector3.forward
+ };
+ }
+
+ #endregion RCC Stuff
+
+ #region Rotation Tracking
+
+ private readonly List _trackedTransforms = new();
+ private readonly List _lastPositions = new();
+ private readonly List _totalAngles = new();
+ private bool _isTracking;
+ private float _averageAngle;
+
+ private void StartTrackingTransform(Transform trans)
+ {
+ if (trans == null) return;
+
+ _trackedTransforms.Add(trans);
+ _lastPositions.Add(GetLocalPositionWithoutRotation(transform.position));
+ _totalAngles.Add(0f);
+ _isTracking = true;
+ }
+
+ private void StopTrackingTransform(Transform trans)
+ {
+ int index = _trackedTransforms.IndexOf(trans);
+ if (index != -1)
+ {
+ _trackedTransforms.RemoveAt(index);
+ _lastPositions.RemoveAt(index);
+ _totalAngles.RemoveAt(index);
+ }
+
+ _isTracking = _trackedTransforms.Count > 0;
+ }
+
+ private void UpdateRotationTracking()
+ {
+ if (!_isTracking || _trackedTransforms.Count == 0) return;
+
+ Vector3 trackingAxis = GetSteeringWheelLocalAxis();
+
+ for (int i = 0; i < _trackedTransforms.Count; i++)
+ {
+ if (_trackedTransforms[i] == null) continue;
+
+ Vector3 currentPosition = GetLocalPositionWithoutRotation(_trackedTransforms[i].position);
+ if (currentPosition == _lastPositions[i]) continue;
+
+ Vector3 previousVector = _lastPositions[i];
+ Vector3 currentVector = currentPosition;
+
+ previousVector = Vector3.ProjectOnPlane(previousVector, trackingAxis).normalized;
+ currentVector = Vector3.ProjectOnPlane(currentVector, trackingAxis).normalized;
+
+ if (previousVector.sqrMagnitude > 0.001f && currentVector.sqrMagnitude > 0.001f)
+ {
+ float deltaAngle = Vector3.SignedAngle(previousVector, currentVector, trackingAxis);
+ if (Mathf.Abs(deltaAngle) < 90f) _totalAngles[i] += deltaAngle; // Prevent big tracking jumps
+ }
+
+ _lastPositions[i] = currentPosition;
+ }
+
+ // Calculate average every frame using only valid transforms
+ float sumAngles = 0f;
+ int validTransforms = 0;
+
+ for (int i = 0; i < _trackedTransforms.Count; i++)
+ {
+ if (_trackedTransforms[i] == null) continue;
+ sumAngles += _totalAngles[i];
+ validTransforms++;
+ }
+
+ if (validTransforms > 0)
+ _averageAngle = sumAngles / validTransforms;
+ }
+
+ private float GetNormalizedValue()
+ {
+ float maxRange = GetMaxSteeringRange();
+ // return Mathf.Clamp(_averageAngle / (maxRange * 0.5f), -1f, 1f);
+ return Mathf.Clamp(_averageAngle / (maxRange), -1f, 1f) * GetSteeringWheelSign();
+ }
+
+ private Vector3 GetLocalPositionWithoutRotation(Vector3 worldPosition)
+ {
+ Transform steeringTransform = _carController.SteeringWheel;
+
+ Quaternion localRotation = steeringTransform.localRotation;
+ steeringTransform.localRotation = _carController.orgSteeringWheelRot;
+
+ Vector3 localPosition = steeringTransform.InverseTransformPoint(worldPosition);
+ steeringTransform.localRotation = localRotation;
+
+ return localPosition;
+ }
+
+ #endregion Rotation Tracking
+
+ public override void OnGrab(InteractionContext context, Vector3 grabPoint)
+ {
+ if (ControllerRay?.pivotPoint == null)
+ return;
+
+ StartTrackingTransform(ControllerRay.transform);
+ }
+
+ public override void OnDrop(InteractionContext context)
+ {
+ if (ControllerRay?.transform != null)
+ StopTrackingTransform(ControllerRay.transform);
+ }
+
+ private void Update()
+ {
+ if (!IsPickedUp || ControllerRay?.pivotPoint == null || _carController == null)
+ return;
+
+ UpdateRotationTracking();
+ }
+
+ #region Unused Abstract Method Implementations
+
+ public override void OnUseDown(InteractionContext context) { }
+ public override void OnUseUp(InteractionContext context) { }
+ public override void OnFlingTowardsTarget(Vector3 target) { }
+
+ #endregion Unused Abstract Method Implementations
+}
\ No newline at end of file
diff --git a/GrabbableSteeringWheel/Main.cs b/GrabbableSteeringWheel/Main.cs
new file mode 100644
index 0000000..1ced7e0
--- /dev/null
+++ b/GrabbableSteeringWheel/Main.cs
@@ -0,0 +1,61 @@
+using MelonLoader;
+using NAK.GrabbableSteeringWheel.Patches;
+
+namespace NAK.GrabbableSteeringWheel;
+
+public class GrabbableSteeringWheelMod : MelonMod
+{
+ internal static MelonLogger.Instance Logger;
+
+ #region Melon Preferences
+
+ // private static readonly MelonPreferences_Category Category =
+ // MelonPreferences.CreateCategory(nameof(GrabbableSteeringWheelMod));
+ //
+ // private static readonly MelonPreferences_Entry EntryEnabled =
+ // Category.CreateEntry(
+ // "use_legacy_mitigation",
+ // true,
+ // "Enabled",
+ // description: "Enable legacy content camera hack when in Legacy worlds.");
+
+ #endregion Melon Preferences
+
+ #region Melon Events
+
+ public override void OnInitializeMelon()
+ {
+ Logger = LoggerInstance;
+
+ ApplyPatches(typeof(RCCCarControllerV3_Patches));
+ ApplyPatches(typeof(CVRInputManager_Patches));
+ }
+
+ #endregion Melon Events
+
+ #region Melon Mod Utilities
+
+ private static void InitializeIntegration(string modName, Action integrationAction)
+ {
+ if (RegisteredMelons.All(it => it.Info.Name != modName))
+ return;
+
+ Logger.Msg($"Initializing {modName} integration.");
+ integrationAction.Invoke();
+ }
+
+ private void ApplyPatches(Type type)
+ {
+ try
+ {
+ HarmonyInstance.PatchAll(type);
+ }
+ catch (Exception e)
+ {
+ LoggerInstance.Msg($"Failed while patching {type.Name}!");
+ LoggerInstance.Error(e);
+ }
+ }
+
+ #endregion Melon Mod Utilities
+}
\ No newline at end of file
diff --git a/GrabbableSteeringWheel/ModSettings.cs b/GrabbableSteeringWheel/ModSettings.cs
new file mode 100644
index 0000000..26a56be
--- /dev/null
+++ b/GrabbableSteeringWheel/ModSettings.cs
@@ -0,0 +1,24 @@
+using MelonLoader;
+
+namespace NAK.GrabbableSteeringWheel;
+
+internal static class ModSettings
+{
+ #region Constants
+
+ internal const string ModName = nameof(GrabbableSteeringWheel);
+ // internal const string LCM_SettingsCategory = "Legacy Content Mitigation";
+
+ #endregion Constants
+
+ #region Melon Preferences
+
+ private static readonly MelonPreferences_Category Category =
+ MelonPreferences.CreateCategory(ModName);
+
+ // internal static readonly MelonPreferences_Entry EntryAutoForLegacyWorlds =
+ // Category.CreateEntry("auto_for_legacy_worlds", true,
+ // "Auto For Legacy Worlds", description: "Should Legacy View be auto enabled for detected Legacy worlds?");
+ //
+ #endregion Melon Preferences
+}
\ No newline at end of file
diff --git a/RCCVirtualSteeringWheel/Patches.cs b/GrabbableSteeringWheel/Patches.cs
similarity index 62%
rename from RCCVirtualSteeringWheel/Patches.cs
rename to GrabbableSteeringWheel/Patches.cs
index c4824ce..c6a828c 100644
--- a/RCCVirtualSteeringWheel/Patches.cs
+++ b/GrabbableSteeringWheel/Patches.cs
@@ -1,21 +1,21 @@
using ABI_RC.Systems.InputManagement;
using ABI_RC.Systems.Movement;
using HarmonyLib;
-using NAK.RCCVirtualSteeringWheel.Util;
using UnityEngine;
-namespace NAK.RCCVirtualSteeringWheel.Patches;
+namespace NAK.GrabbableSteeringWheel.Patches;
internal static class RCCCarControllerV3_Patches
{
[HarmonyPostfix]
[HarmonyPatch(typeof(RCC_CarControllerV3), nameof(RCC_CarControllerV3.Awake))]
- private static void Postfix_RCC_CarControllerV3_Awake(RCC_CarControllerV3 __instance)
+ private static void Postfix_RCC_CarControllerV3_Awake(ref RCC_CarControllerV3 __instance)
{
Transform steeringWheelTransform = __instance.SteeringWheel;
if (steeringWheelTransform == null)
return;
+ RCC_CarControllerV3 v3 = __instance;
BoneVertexBoundsUtility.CalculateBoneWeightedBounds(
steeringWheelTransform,
0.8f,
@@ -25,7 +25,12 @@ internal static class RCCCarControllerV3_Patches
if (!result.IsValid)
return;
- SteeringWheelRoot.SetupSteeringWheel(__instance, result.LocalBounds);
+ BoxCollider boxCollider = steeringWheelTransform.gameObject.AddComponent();
+ boxCollider.center = result.LocalBounds.center;
+ boxCollider.size = result.LocalBounds.size;
+
+ SteeringWheelPickup steeringWheel = steeringWheelTransform.gameObject.AddComponent();
+ steeringWheel.SetupSteeringWheel(v3);
});
}
}
@@ -37,11 +42,8 @@ internal static class CVRInputManager_Patches
private static void Postfix_CVRInputManager_UpdateInput(ref CVRInputManager __instance)
{
// Steering input is clamped in RCC component
- if (BetterBetterCharacterController.Instance.IsSittingOnControlSeat()
- && SteeringWheelRoot.TryGetWheelInput(
- BetterBetterCharacterController.Instance._lastCvrSeat._carController, out float steeringValue))
- {
- __instance.steering = steeringValue;
- }
+ if (BetterBetterCharacterController.Instance.IsSittingOnControlSeat())
+ __instance.steering += SteeringWheelPickup.GetSteerInput(
+ BetterBetterCharacterController.Instance._lastCvrSeat._carController);
}
}
\ No newline at end of file
diff --git a/RCCVirtualSteeringWheel/Properties/AssemblyInfo.cs b/GrabbableSteeringWheel/Properties/AssemblyInfo.cs
similarity index 71%
rename from RCCVirtualSteeringWheel/Properties/AssemblyInfo.cs
rename to GrabbableSteeringWheel/Properties/AssemblyInfo.cs
index c82b5de..6441f79 100644
--- a/RCCVirtualSteeringWheel/Properties/AssemblyInfo.cs
+++ b/GrabbableSteeringWheel/Properties/AssemblyInfo.cs
@@ -1,21 +1,21 @@
using System.Reflection;
using MelonLoader;
-using NAK.RCCVirtualSteeringWheel;
-using NAK.RCCVirtualSteeringWheel.Properties;
+using NAK.GrabbableSteeringWheel;
+using NAK.GrabbableSteeringWheel.Properties;
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
-[assembly: AssemblyTitle(nameof(NAK.RCCVirtualSteeringWheel))]
+[assembly: AssemblyTitle(nameof(NAK.GrabbableSteeringWheel))]
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
-[assembly: AssemblyProduct(nameof(NAK.RCCVirtualSteeringWheel))]
+[assembly: AssemblyProduct(nameof(NAK.GrabbableSteeringWheel))]
[assembly: MelonInfo(
- typeof(RCCVirtualSteeringWheelMod),
- nameof(NAK.RCCVirtualSteeringWheel),
+ typeof(GrabbableSteeringWheelMod),
+ nameof(NAK.GrabbableSteeringWheel),
AssemblyInfoParams.Version,
AssemblyInfoParams.Author,
- downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/RCCVirtualSteeringWheel"
+ downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/GrabbableSteeringWheel"
)]
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
@@ -25,7 +25,7 @@ using NAK.RCCVirtualSteeringWheel.Properties;
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
[assembly: HarmonyDontPatchAll]
-namespace NAK.RCCVirtualSteeringWheel.Properties;
+namespace NAK.GrabbableSteeringWheel.Properties;
internal static class AssemblyInfoParams
{
public const string Version = "1.0.0";
diff --git a/RCCVirtualSteeringWheel/README.md b/GrabbableSteeringWheel/README.md
similarity index 95%
rename from RCCVirtualSteeringWheel/README.md
rename to GrabbableSteeringWheel/README.md
index b396266..08942df 100644
--- a/RCCVirtualSteeringWheel/README.md
+++ b/GrabbableSteeringWheel/README.md
@@ -1,7 +1,7 @@
-# RCCVirtualSteeringWheel
+# GrabbableSteeringWheel
Experiment with making rigged RCC vehicle steering wheels work for VR steering input.
-
+~~~~
---
Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI.
@@ -12,4 +12,3 @@ https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games
> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use.
> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive.
-~~~~
\ No newline at end of file
diff --git a/RCCVirtualSteeringWheel/format.json b/GrabbableSteeringWheel/format.json
similarity index 100%
rename from RCCVirtualSteeringWheel/format.json
rename to GrabbableSteeringWheel/format.json
diff --git a/LegacyContentMitigation/Components/FakeMultiPassHack.cs b/LegacyContentMitigation/Components/FakeMultiPassHack.cs
index fcc7169..31924db 100644
--- a/LegacyContentMitigation/Components/FakeMultiPassHack.cs
+++ b/LegacyContentMitigation/Components/FakeMultiPassHack.cs
@@ -207,11 +207,9 @@ public class FakeMultiPassHack : MonoBehaviour
eyeCamera.targetTexture = targetTexture;
eyeCamera.cullingMask = CachedCullingMask;
eyeCamera.stereoTargetEye = StereoTargetEyeMask.None;
- eyeCamera.cullingMatrix = _mainCamera.cullingMatrix;
eyeCamera.projectionMatrix = _mainCamera.GetStereoProjectionMatrix(eye);
eyeCamera.worldToCameraMatrix = _mainCamera.GetStereoViewMatrix(eye);
eyeCamera.Render();
- eyeCamera.ResetCullingMatrix();
}
}
diff --git a/LegacyContentMitigation/Main.cs b/LegacyContentMitigation/Main.cs
index 5d1c1b9..8db0910 100644
--- a/LegacyContentMitigation/Main.cs
+++ b/LegacyContentMitigation/Main.cs
@@ -33,7 +33,6 @@ public class LegacyContentMitigationMod : MelonMod
ApplyPatches(typeof(Patches.CVRWorld_Patches)); // post processing shit
ApplyPatches(typeof(Patches.CVRTools_Patches)); // prevent shader replacement when fix is active
ApplyPatches(typeof(Patches.HeadHiderManager_Patches)); // prevent main cam triggering early head hide
- ApplyPatches(typeof(Patches.CVRMirror_Patches));
InitializeIntegration("BTKUILib", Integrations.BtkUiAddon.Initialize); // quick menu options
}
diff --git a/LegacyContentMitigation/Patches.cs b/LegacyContentMitigation/Patches.cs
index 2b151e8..e2dabaf 100644
--- a/LegacyContentMitigation/Patches.cs
+++ b/LegacyContentMitigation/Patches.cs
@@ -6,8 +6,6 @@ using ABI_RC.Core.Player;
using ABI_RC.Core.Player.LocalClone;
using ABI_RC.Core.Player.TransformHider;
using ABI.CCK.Components;
-using cohtml;
-using cohtml.Net;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Rendering.PostProcessing;
@@ -127,15 +125,4 @@ internal static class HeadHiderManager_Patches
transformHiderManager._resetAfterThisRender = flag;
}
}
-}
-
-internal static class CVRMirror_Patches
-{
- [HarmonyPostfix]
- [HarmonyPatch(typeof(CVRMirror), nameof(CVRMirror.CopyCameraProperties))]
- private static void Postfix_CVRMirror_CopyCameraProperties(ref CVRMirror __instance)
- {
- __instance.m_ReflectionCamera.ResetCullingMatrix();
- }
-
}
\ No newline at end of file
diff --git a/LegacyContentMitigation/Properties/AssemblyInfo.cs b/LegacyContentMitigation/Properties/AssemblyInfo.cs
index 5969d71..e460687 100644
--- a/LegacyContentMitigation/Properties/AssemblyInfo.cs
+++ b/LegacyContentMitigation/Properties/AssemblyInfo.cs
@@ -27,6 +27,6 @@ using System.Reflection;
namespace NAK.LegacyContentMitigation.Properties;
internal static class AssemblyInfoParams
{
- public const string Version = "1.0.2";
+ public const string Version = "1.0.1";
public const string Author = "Exterrata & NotAKidoS";
}
\ No newline at end of file
diff --git a/RCCVirtualSteeringWheel/Main.cs b/RCCVirtualSteeringWheel/Main.cs
deleted file mode 100644
index 79fae31..0000000
--- a/RCCVirtualSteeringWheel/Main.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using MelonLoader;
-using NAK.RCCVirtualSteeringWheel.Patches;
-
-namespace NAK.RCCVirtualSteeringWheel;
-
-public class RCCVirtualSteeringWheelMod : MelonMod
-{
- private static MelonLogger.Instance Logger;
-
- #region Melon Events
-
- public override void OnInitializeMelon()
- {
- Logger = LoggerInstance;
-
- ApplyPatches(typeof(RCCCarControllerV3_Patches));
- ApplyPatches(typeof(CVRInputManager_Patches));
-
- Logger.Msg(ModSettings.EntryCustomSteeringRange);
- }
-
- #endregion Melon Events
-
- #region Melon Mod Utilities
-
- private void ApplyPatches(Type type)
- {
- try
- {
- HarmonyInstance.PatchAll(type);
- }
- catch (Exception e)
- {
- LoggerInstance.Msg($"Failed while patching {type.Name}!");
- LoggerInstance.Error(e);
- }
- }
-
- #endregion Melon Mod Utilities
-}
\ No newline at end of file
diff --git a/RCCVirtualSteeringWheel/ModSettings.cs b/RCCVirtualSteeringWheel/ModSettings.cs
deleted file mode 100644
index f07436c..0000000
--- a/RCCVirtualSteeringWheel/ModSettings.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using MelonLoader;
-
-namespace NAK.RCCVirtualSteeringWheel;
-
-internal static class ModSettings
-{
- #region Constants
-
- private const string ModName = nameof(RCCVirtualSteeringWheel);
-
- #endregion Constants
-
- #region Melon Preferences
-
- private static readonly MelonPreferences_Category Category =
- MelonPreferences.CreateCategory(ModName);
-
- internal static readonly MelonPreferences_Entry EntryOverrideSteeringRange =
- Category.CreateEntry("override_steering_range", false,
- "Override Steering Range", description: "Should the steering wheel use a custom steering range instead of the vehicle's default?");
-
- internal static readonly MelonPreferences_Entry EntryCustomSteeringRange =
- Category.CreateEntry("custom_steering_range", 60f,
- "Custom Steering Range", description: "The custom steering range in degrees when override is enabled (default: 60)");
-
- internal static readonly MelonPreferences_Entry EntryInvertSteering =
- Category.CreateEntry("invert_steering", false,
- "Invert Steering", description: "Inverts the steering direction");
-
- #endregion Melon Preferences
-}
\ No newline at end of file
diff --git a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Components/SteeringWheelPickup.cs b/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Components/SteeringWheelPickup.cs
deleted file mode 100644
index d809150..0000000
--- a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Components/SteeringWheelPickup.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using ABI_RC.Core.InteractionSystem;
-using ABI_RC.Core.InteractionSystem.Base;
-using UnityEngine;
-
-namespace NAK.RCCVirtualSteeringWheel;
-
-public class SteeringWheelPickup : Pickupable
-{
- #region Public Properties
- public override bool DisallowTheft => true;
- public override float MaxGrabDistance => 0.8f;
- public override float MaxPushDistance => 0f;
- public override bool IsAutoHold => false;
- public override bool IsObjectRotationAllowed => false;
- public override bool IsObjectPushPullAllowed => false;
- public override bool IsObjectUseAllowed => false;
- public override bool CanPickup => IsPickupable && !IsPickedUp;
- internal SteeringWheelRoot root;
- #endregion
-
- #region Public Methods
- public override void OnUseDown(InteractionContext context) { }
- public override void OnUseUp(InteractionContext context) { }
- public override void OnFlingTowardsTarget(Vector3 target) { }
-
- public override void OnGrab(InteractionContext context, Vector3 grabPoint) {
- if (ControllerRay?.pivotPoint != null)
- root.StartTrackingTransform(ControllerRay.transform);
- }
-
- public override void OnDrop(InteractionContext context) {
- if (ControllerRay?.transform != null)
- root.StopTrackingTransform(ControllerRay.transform);
- }
- #endregion
-}
\ No newline at end of file
diff --git a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Components/SteeringWheelRoot.cs b/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Components/SteeringWheelRoot.cs
deleted file mode 100644
index cc293aa..0000000
--- a/RCCVirtualSteeringWheel/RCCVirtualSteeringWheel/Components/SteeringWheelRoot.cs
+++ /dev/null
@@ -1,313 +0,0 @@
-using UnityEngine;
-
-namespace NAK.RCCVirtualSteeringWheel;
-
-public class SteeringWheelRoot : MonoBehaviour
-{
- #region Static Variables
-
- private static readonly Dictionary ActiveWheels = new();
-
- #endregion Static Variables
-
- #region Static Methods
-
- public static bool TryGetWheelInput(RCC_CarControllerV3 carController, out float steeringInput)
- {
- if (ActiveWheels.TryGetValue(carController, out SteeringWheelRoot wheel) && wheel._averageAngle != 0f)
- {
- steeringInput = wheel.GetNormalizedValue();
- return true;
- }
-
- steeringInput = 0f;
- return false;
- }
-
- public static void SetupSteeringWheel(RCC_CarControllerV3 carController, Bounds steeringWheelBounds)
- {
- Transform steeringWheel = carController.SteeringWheel;
- if (carController == null) return;
-
- SteeringWheelRoot wheel = steeringWheel.gameObject.AddComponent();
- wheel._carController = carController;
-
- Array.Resize(ref wheel._pickups, 2);
- CreatePickup(out wheel._pickups[0]);
- CreatePickup(out wheel._pickups[1]);
-
- return;
-
- void CreatePickup(out SteeringWheelPickup wheelPickup)
- {
- GameObject pickup = new()
- {
- transform =
- {
- parent = steeringWheel.transform,
- localPosition = Vector3.zero,
- localRotation = Quaternion.identity,
- localScale = Vector3.one
- }
- };
-
- BoxCollider collider = pickup.AddComponent();
- collider.size = steeringWheelBounds.size;
- collider.center = steeringWheelBounds.center;
-
- wheelPickup = pickup.AddComponent();
- wheelPickup.root = wheel;
- }
- }
-
- #endregion Static Methods
-
- #region Public Properties
-
- private bool IsWheelBeingHeld => _pickups[0].IsPickedUp || _pickups[1].IsPickedUp;
-
- #endregion Public Properties
-
- #region Private Variables
-
- private RCC_CarControllerV3 _carController;
- private SteeringWheelPickup[] _pickups;
-
- private float _originalSteeringWheelAngleMultiplier;
- private float _originalSteeringWheelSign;
-
- private readonly List _trackedTransforms = new();
- private readonly List _lastPositions = new();
- private readonly List _totalAngles = new();
-
- private bool _isTracking;
- private float _averageAngle;
- private float _timeWheelReleased = -1f;
- private const float RETURN_TO_CENTER_DURATION = 2f;
-
- #endregion Private Variables
-
- #region Unity Events
-
- private void Start()
- {
- ActiveWheels.TryAdd(_carController, this);
- InitializeWheel();
- }
-
- private void Update()
- {
- if (_carController == null) return;
- UpdateWheelState();
- UpdateSteeringBehavior();
- }
-
- private void OnDestroy()
- {
- ActiveWheels.Remove(_carController);
- }
-
- #endregion Unity Events
-
- #region Public Methods
-
- internal void StartTrackingTransform(Transform trans)
- {
- if (trans == null) return;
-
- var currentAngle = 0f;
- if (_isTracking)
- {
- var sum = 0f;
- var validTransforms = 0;
- for (var i = 0; i < _trackedTransforms.Count; i++)
- if (_trackedTransforms[i] != null)
- {
- sum += _totalAngles[i];
- validTransforms++;
- }
-
- if (validTransforms > 0)
- currentAngle = sum / validTransforms;
- }
-
- _trackedTransforms.Add(trans);
- _lastPositions.Add(GetLocalPositionWithoutRotation(transform.position));
- _totalAngles.Add(currentAngle);
- _isTracking = true;
- }
-
- internal void StopTrackingTransform(Transform trans)
- {
- var index = _trackedTransforms.IndexOf(trans);
- if (index == -1) return;
-
- var currentAverage = CalculateCurrentAverage();
- _trackedTransforms.RemoveAt(index);
- _lastPositions.RemoveAt(index);
- _totalAngles.RemoveAt(index);
-
- if (_trackedTransforms.Count <= 0)
- return;
-
- for (var i = 0; i < _totalAngles.Count; i++)
- _totalAngles[i] = currentAverage;
- }
-
- private float CalculateCurrentAverage()
- {
- var sum = 0f;
- var validTransforms = 0;
- for (var i = 0; i < _trackedTransforms.Count; i++)
- if (_trackedTransforms[i] != null)
- {
- sum += _totalAngles[i];
- validTransforms++;
- }
-
- return validTransforms > 0 ? sum / validTransforms : 0f;
- }
-
- #endregion Public Methods
-
- #region Private Methods
-
- private void InitializeWheel()
- {
- _originalSteeringWheelAngleMultiplier = _carController.steeringWheelAngleMultiplier;
- _originalSteeringWheelSign = Mathf.Sign(_originalSteeringWheelAngleMultiplier);
- _carController.useCounterSteering = false;
- _carController.useSteeringSmoother = false;
- }
-
- private void UpdateWheelState()
- {
- var isHeld = IsWheelBeingHeld;
- if (!isHeld && _timeWheelReleased < 0f)
- _timeWheelReleased = Time.time;
- else if (isHeld)
- _timeWheelReleased = -1f;
- }
-
- private void UpdateSteeringBehavior()
- {
- UpdateSteeringMultiplier();
-
- if (IsWheelBeingHeld)
- UpdateRotationTracking();
- else if (_timeWheelReleased >= 0f)
- HandleWheelReturn();
- }
-
- private void UpdateSteeringMultiplier()
- {
- _carController.steeringWheelAngleMultiplier = ModSettings.EntryOverrideSteeringRange.Value
- ? ModSettings.EntryCustomSteeringRange.Value * _originalSteeringWheelSign / _carController.steerAngle
- : _originalSteeringWheelAngleMultiplier;
- }
-
- private void HandleWheelReturn()
- {
- var timeSinceRelease = Time.time - _timeWheelReleased;
- if (timeSinceRelease < RETURN_TO_CENTER_DURATION)
- {
- var t = timeSinceRelease / RETURN_TO_CENTER_DURATION;
- _averageAngle = Mathf.Lerp(_averageAngle, 0f, t);
-
- for (var i = 0; i < _totalAngles.Count; i++)
- _totalAngles[i] = _averageAngle;
- }
- else
- {
- _averageAngle = 0f;
- for (var i = 0; i < _totalAngles.Count; i++)
- _totalAngles[i] = 0f;
- }
- }
-
- private float GetMaxSteeringRange()
- {
- return _carController.steerAngle * Mathf.Abs(_carController.steeringWheelAngleMultiplier);
- }
-
- private float GetSteeringWheelSign()
- {
- return _originalSteeringWheelSign * (ModSettings.EntryInvertSteering.Value ? 1f : -1f);
- }
-
- private Vector3 GetSteeringWheelLocalAxis()
- {
- return _carController.steeringWheelRotateAround switch
- {
- RCC_CarControllerV3.SteeringWheelRotateAround.XAxis => Vector3.right,
- RCC_CarControllerV3.SteeringWheelRotateAround.YAxis => Vector3.up,
- RCC_CarControllerV3.SteeringWheelRotateAround.ZAxis => Vector3.forward,
- _ => Vector3.forward
- };
- }
-
- private Vector3 GetLocalPositionWithoutRotation(Vector3 worldPosition)
- {
- Transform steeringTransform = _carController.SteeringWheel;
- Quaternion localRotation = steeringTransform.localRotation;
- steeringTransform.localRotation = _carController.orgSteeringWheelRot;
- Vector3 localPosition = steeringTransform.InverseTransformPoint(worldPosition);
- steeringTransform.localRotation = localRotation;
- return localPosition;
- }
-
- private float GetNormalizedValue()
- {
- return Mathf.Clamp(_averageAngle / GetMaxSteeringRange(), -1f, 1f) * GetSteeringWheelSign();
- }
-
- private void UpdateRotationTracking()
- {
- if (!_isTracking || _trackedTransforms.Count == 0) return;
-
- Vector3 trackingAxis = GetSteeringWheelLocalAxis();
- UpdateTransformAngles(trackingAxis);
- UpdateAverageAngle();
- }
-
- private void UpdateTransformAngles(Vector3 trackingAxis)
- {
- for (var i = 0; i < _trackedTransforms.Count; i++)
- {
- if (_trackedTransforms[i] == null) continue;
-
- Vector3 currentPosition = GetLocalPositionWithoutRotation(_trackedTransforms[i].position);
- if (currentPosition == _lastPositions[i]) continue;
-
- Vector3 previousVector = Vector3.ProjectOnPlane(_lastPositions[i], trackingAxis).normalized;
- Vector3 currentVector = Vector3.ProjectOnPlane(currentPosition, trackingAxis).normalized;
-
- if (previousVector.sqrMagnitude > 0.001f && currentVector.sqrMagnitude > 0.001f)
- {
- var deltaAngle = Vector3.SignedAngle(previousVector, currentVector, trackingAxis);
- if (Mathf.Abs(deltaAngle) < 90f)
- _totalAngles[i] += deltaAngle;
- }
-
- _lastPositions[i] = currentPosition;
- }
- }
-
- private void UpdateAverageAngle()
- {
- var sumAngles = 0f;
- var validTransforms = 0;
-
- for (var i = 0; i < _trackedTransforms.Count; i++)
- {
- if (_trackedTransforms[i] == null) continue;
- sumAngles += _totalAngles[i];
- validTransforms++;
- }
-
- if (validTransforms > 0)
- _averageAngle = sumAngles / validTransforms;
- }
-
- #endregion Private Methods
-}
\ No newline at end of file