-
Fix animation overrides (chairs, etc.):
+
Fix animator overrides (chairs, etc.):
diff --git a/ml_drs/Properties/AssemblyInfo.cs b/ml_drs/Properties/AssemblyInfo.cs
index 31fc69a..7154274 100644
--- a/ml_drs/Properties/AssemblyInfo.cs
+++ b/ml_drs/Properties/AssemblyInfo.cs
@@ -1,4 +1,6 @@
-[assembly: MelonLoader.MelonInfo(typeof(ml_drs.DesktopReticleSwitch), "DesktopReticleSwitch", "1.0.0-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
+using System.Reflection;
+
+[assembly: MelonLoader.MelonInfo(typeof(ml_drs.DesktopReticleSwitch), "DesktopReticleSwitch", "1.0.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
\ No newline at end of file
diff --git a/ml_drs/ml_drs.csproj b/ml_drs/ml_drs.csproj
index 05af04f..a8b90e0 100644
--- a/ml_drs/ml_drs.csproj
+++ b/ml_drs/ml_drs.csproj
@@ -29,8 +29,7 @@
false
- D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\MelonLoader.dll
- false
+ D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll
D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll
diff --git a/ml_egn/Main.cs b/ml_egn/Main.cs
index c77bf82..90d54a2 100644
--- a/ml_egn/Main.cs
+++ b/ml_egn/Main.cs
@@ -1,5 +1,4 @@
using ABI_RC.Core.EventSystem;
-using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.IO;
using ABI_RC.Core.Networking;
using ABI_RC.Core.Util;
@@ -57,6 +56,7 @@ namespace ml_egn
Utils.ShowMenuNotification("Avatar changed", 1f);
else
Utils.ShowHUDNotification("(Synced) Client", "Avatar changed");
+
}
catch(System.Exception e)
{
@@ -68,19 +68,37 @@ namespace ml_egn
{
try
{
- if(Utils.IsConnected())
+ if(Utils.ArePropsEnabled())
{
- if(Utils.IsMenuOpened())
- Utils.ShowMenuNotification("Prop spawned", 1f);
+ if(Utils.ArePropsAllowed())
+ {
+ if(Utils.IsConnected())
+ {
+ if(Utils.IsMenuOpened())
+ Utils.ShowMenuNotification("Prop spawned", 1f);
+ else
+ Utils.ShowHUDNotification("(Synced) Client", "Prop spawned");
+ }
+ else
+ {
+ if(Utils.IsMenuOpened())
+ Utils.ShowMenuAlert("Prop Error", "Not connected to live instance");
+ else
+ Utils.ShowHUDNotification("(Local) Client", "Unable to spawn prop", "Not connected to live instance");
+ }
+ }
else
- Utils.ShowHUDNotification("(Synced) Client", "Prop spawned");
+ {
+ if(Utils.IsMenuOpened())
+ Utils.ShowMenuAlert("Prop Error", "Props are not allowed in this world");
+ }
}
else
{
if(Utils.IsMenuOpened())
- Utils.ShowMenuAlert("Prop Error", "Not connected to live instance");
+ Utils.ShowMenuAlert("Prop Error", "Props are disabled in game settings");
else
- Utils.ShowHUDNotification("(Local) Client", "Unable to spawn prop", "Not connected to live instance");
+ Utils.ShowHUDNotification("(Local) Client", "Unable to spawn prop", "Props are disabled in game settings");
}
}
catch(System.Exception e)
diff --git a/ml_egn/Properties/AssemblyInfo.cs b/ml_egn/Properties/AssemblyInfo.cs
index b1c8d98..f0961c3 100644
--- a/ml_egn/Properties/AssemblyInfo.cs
+++ b/ml_egn/Properties/AssemblyInfo.cs
@@ -1,4 +1,6 @@
-[assembly: MelonLoader.MelonInfo(typeof(ml_egn.ExtendedGameNotifications), "ExtendedGameNotifications", "1.0.1-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
+using System.Reflection;
+
+[assembly: MelonLoader.MelonInfo(typeof(ml_egn.ExtendedGameNotifications), "ExtendedGameNotifications", "1.0.2", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
\ No newline at end of file
diff --git a/ml_egn/Utils.cs b/ml_egn/Utils.cs
index f4935b9..319e7ac 100644
--- a/ml_egn/Utils.cs
+++ b/ml_egn/Utils.cs
@@ -1,5 +1,6 @@
using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Networking;
+using ABI_RC.Core.Savior;
using ABI_RC.Core.UI;
using DarkRift;
@@ -42,5 +43,8 @@ namespace ml_egn
l_result = (NetworkManager.Instance.GameNetwork.ConnectionState == ConnectionState.Connected);
return l_result;
}
+
+ public static bool ArePropsAllowed() => ((MetaPort.Instance != null) && MetaPort.Instance.worldAllowProps);
+ public static bool ArePropsEnabled() => ((MetaPort.Instance != null) && MetaPort.Instance.settings.GetSettingsBool("ContentFilterPropsEnabled"));
}
}
diff --git a/ml_egn/ml_egn.csproj b/ml_egn/ml_egn.csproj
index 1bde6ae..be33045 100644
--- a/ml_egn/ml_egn.csproj
+++ b/ml_egn/ml_egn.csproj
@@ -4,7 +4,7 @@
netstandard2.1
x64
ExtendedGameNotifications
- 1.0.1
+ 1.0.2
SDraw
None
ExtendedGameNotifications
@@ -21,8 +21,7 @@
- D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\0Harmony.dll
- false
+ D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll
D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll
@@ -41,8 +40,7 @@
false
- D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\MelonLoader.dll
- false
+ D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll
D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll
diff --git a/ml_fpt/Main.cs b/ml_fpt/Main.cs
deleted file mode 100644
index f748e3a..0000000
--- a/ml_fpt/Main.cs
+++ /dev/null
@@ -1,313 +0,0 @@
-using ABI.CCK.Scripts;
-using ABI_RC.Core.InteractionSystem;
-using ABI_RC.Core.Player;
-using ABI_RC.Core.Savior;
-using ABI_RC.Core.UI;
-using ABI_RC.Systems.IK;
-using ABI_RC.Systems.IK.SubSystems;
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using UnityEngine;
-
-namespace ml_fpt
-{
- public class FourPointTracking : MelonLoader.MelonMod
- {
- static FourPointTracking ms_instance = null;
-
- bool m_ready = false;
-
- IndexIK m_indexIK = null;
- RootMotion.FinalIK.VRIK m_vrIK = null;
- RuntimeAnimatorController m_runtimeAnimator = null;
- List m_aasParameters = null;
-
- bool m_calibrationActive = false;
- object m_calibrationTask = null;
-
- int m_hipsTrackerIndex = -1;
- Transform m_hips = null;
-
- Dictionary> m_avatarCalibrations = null;
-
- public override void OnInitializeMelon()
- {
- if(ms_instance == null)
- ms_instance = this;
-
- m_avatarCalibrations = new Dictionary>();
-
- HarmonyInstance.Patch(
- typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)),
- null,
- new HarmonyLib.HarmonyMethod(typeof(FourPointTracking).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
- );
- HarmonyInstance.Patch(
- typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)),
- null,
- new HarmonyLib.HarmonyMethod(typeof(FourPointTracking).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
- );
-
- MelonLoader.MelonCoroutines.Start(WaitForMainMenuView());
- MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
- }
-
- System.Collections.IEnumerator WaitForMainMenuView()
- {
- while(ViewManager.Instance == null)
- yield return null;
- while(ViewManager.Instance.gameMenuView == null)
- yield return null;
- while(ViewManager.Instance.gameMenuView.Listener == null)
- yield return null;
-
- ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () =>
- {
- ViewManager.Instance.gameMenuView.View.RegisterForEvent("MelonMod_FPT_Action_Calibrate", new Action(this.StartCalibration));
- };
-
- ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
- {
- ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("menu.js"));
- };
- }
-
- System.Collections.IEnumerator WaitForLocalPlayer()
- {
- while(PlayerSetup.Instance == null)
- yield return null;
-
- m_indexIK = PlayerSetup.Instance.gameObject.GetComponent();
-
- m_ready = true;
- }
-
- public override void OnDeinitializeMelon()
- {
- if(ms_instance == this)
- ms_instance = null;
-
- m_ready = false;
- m_aasParameters?.Clear();
- m_aasParameters = null;
- m_avatarCalibrations?.Clear();
- m_avatarCalibrations = null;
- m_hipsTrackerIndex = -1;
-
- if(m_calibrationTask != null)
- MelonLoader.MelonCoroutines.Stop(m_calibrationTask);
- m_calibrationTask = null;
- }
-
- void StartCalibration()
- {
- if(m_ready && !m_calibrationActive && PlayerSetup.Instance._inVr && !PlayerSetup.Instance.avatarIsLoading && PlayerSetup.Instance._animator.isHuman && !BodySystem.isCalibrating && !BodySystem.isCalibratedAsFullBody)
- {
- m_hipsTrackerIndex = GetHipsTracker();
- if(m_hipsTrackerIndex != -1)
- {
- m_avatarCalibrations.Remove(MetaPort.Instance.currentAvatarGuid);
-
- m_runtimeAnimator = PlayerSetup.Instance._animator.runtimeAnimatorController;
- m_aasParameters = PlayerSetup.Instance.animatorManager.GetAdditionalSettingsCurrent();
- PlayerSetup.Instance._animator.runtimeAnimatorController = PlayerSetup.Instance.tPoseAnimatorController;
- PlayerSetup.Instance.animatorManager.SetAnimator(PlayerSetup.Instance._animator, PlayerSetup.Instance.tPoseAnimatorController);
-
- m_hips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips);
- m_vrIK = PlayerSetup.Instance._animator.GetComponent();
-
- if(m_vrIK != null)
- m_vrIK.solver.OnPreUpdate += this.OverrideIKWeight;
-
- IKSystem.Instance.leftHandModel.SetActive(true);
- IKSystem.Instance.rightHandModel.SetActive(true);
- PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].ShowTracker(true);
- CVR_InteractableManager.enableInteractions = false;
-
- m_calibrationActive = true;
- m_calibrationTask = MelonLoader.MelonCoroutines.Start(CalibrationTask());
-
- ViewManager.Instance.ForceUiStatus(false);
- ShowHudNotification("Calibration started");
- }
- else
- ShowMenuAlert("No hips tracker detected. Check if tracker has waist role in SteamVR settings.");
- }
- else
- ShowMenuAlert("Calibraton requirements aren't met: be in VR, be not in FBT or avatar calibration, humanoid avatar");
- }
-
- System.Collections.IEnumerator CalibrationTask()
- {
- while(m_calibrationActive)
- {
- if(m_vrIK != null)
- m_vrIK.enabled = false;
-
- m_indexIK.enabled = false;
-
- PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].ShowLine(true, m_hips);
-
- if((CVRInputManager.Instance.interactLeftValue > 0.9f) && (CVRInputManager.Instance.interactRightValue > 0.9f))
- {
- PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].target.transform.position = m_hips.position;
- PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].target.transform.rotation = m_hips.rotation;
-
- m_avatarCalibrations.Add(
- MetaPort.Instance.currentAvatarGuid,
- new Tuple(
- PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].target.transform.localPosition,
- PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].target.transform.localRotation
- )
- );
-
- if(m_vrIK != null)
- {
- m_vrIK.solver.spine.pelvisTarget = PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].target;
- m_vrIK.solver.spine.pelvisPositionWeight = 1f;
- m_vrIK.solver.spine.pelvisRotationWeight = 1f;
- m_vrIK.solver.OnPreUpdate -= this.OverrideIKWeight;
- m_vrIK.solver.IKPositionWeight = 1f;
- m_vrIK.enabled = true;
- }
-
- m_indexIK.enabled = true;
-
- PlayerSetup.Instance._animator.runtimeAnimatorController = m_runtimeAnimator;
- PlayerSetup.Instance.animatorManager.SetAnimator(PlayerSetup.Instance._animator, m_runtimeAnimator);
- if(m_aasParameters != null)
- {
- foreach(var l_param in m_aasParameters)
- {
- PlayerSetup.Instance.animatorManager.SetAnimatorParameter(l_param.name, l_param.value);
- }
- }
-
- IKSystem.Instance.leftHandModel.SetActive(false);
- IKSystem.Instance.rightHandModel.SetActive(false);
- PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].ShowTracker(false);
- PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].ShowLine(false);
- CVR_InteractableManager.enableInteractions = true;
-
- Reset();
-
- ShowHudNotification("Calibration completed");
- }
-
- yield return null;
- }
-
- m_calibrationTask = null; // Idk if it's safe or not
- }
-
- void OverrideIKWeight()
- {
- if(m_calibrationActive)
- {
- m_vrIK.solver.IKPositionWeight = 0f;
- }
- }
-
- void Reset()
- {
- m_vrIK = null;
- m_runtimeAnimator = null;
- m_aasParameters = null;
- m_calibrationActive = false;
- m_calibrationTask = null;
- m_hipsTrackerIndex = -1;
- m_hips = null;
- }
-
- static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear();
- void OnAvatarClear()
- {
- try
- {
- if(m_calibrationActive)
- {
- if(m_calibrationTask != null)
- MelonLoader.MelonCoroutines.Stop(m_calibrationTask);
-
- m_indexIK.enabled = true;
-
- IKSystem.Instance.leftHandModel.SetActive(false);
- IKSystem.Instance.rightHandModel.SetActive(false);
-
- if(m_hipsTrackerIndex != -1)
- {
- PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].ShowTracker(false);
- PlayerSetup.Instance._trackerManager.trackers[m_hipsTrackerIndex].ShowLine(false);
- }
- CVR_InteractableManager.enableInteractions = true;
-
- Reset();
-
- ShowHudNotification("Calibration canceled");
- }
- }
- catch(Exception e)
- {
- MelonLoader.MelonLogger.Error(e);
- }
- }
-
- static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar();
- void OnSetupAvatar()
- {
- try
- {
- if(m_ready && PlayerSetup.Instance._inVr && PlayerSetup.Instance._animator.isHuman && !VRTrackerManager.Instance.CheckFullBody())
- {
- int l_hipsTracker = GetHipsTracker();
- if((l_hipsTracker != -1) && m_avatarCalibrations.TryGetValue(MetaPort.Instance.currentAvatarGuid, out var l_stored))
- {
- var l_vrIK = PlayerSetup.Instance._animator.GetComponent();
- if(l_vrIK != null)
- {
- l_vrIK.solver.spine.pelvisTarget = PlayerSetup.Instance._trackerManager.trackers[l_hipsTracker].target;
- l_vrIK.solver.spine.pelvisPositionWeight = 1f;
- l_vrIK.solver.spine.pelvisRotationWeight = 1f;
-
- l_vrIK.solver.spine.pelvisTarget.localPosition = l_stored.Item1;
- l_vrIK.solver.spine.pelvisTarget.localRotation = l_stored.Item2;
-
- ShowHudNotification("Applied saved calibration");
- }
- }
- }
- }
- catch(System.Exception e)
- {
- MelonLoader.MelonLogger.Error(e);
- }
- }
-
- static void ShowHudNotification(string p_message)
- {
- if(CohtmlHud.Instance != null)
- CohtmlHud.Instance.ViewDropText("4-Point Tracking", p_message);
- }
-
- static void ShowMenuAlert(string p_message)
- {
- if(ViewManager.Instance != null)
- ViewManager.Instance.TriggerAlert("4-Point Tracking", p_message, 0, false);
- }
-
- static int GetHipsTracker()
- {
- int l_result = -1;
- for(int i = 0; i < PlayerSetup.Instance._trackerManager.trackerNames.Length; i++)
- {
- if((PlayerSetup.Instance._trackerManager.trackerNames[i] == "vive_tracker_waist") && PlayerSetup.Instance._trackerManager.trackers[i].active)
- {
- l_result = i;
- break;
- }
- }
- return l_result;
- }
- }
-}
\ No newline at end of file
diff --git a/ml_fpt/README.md b/ml_fpt/README.md
deleted file mode 100644
index 8eaeeb7..0000000
--- a/ml_fpt/README.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Four Point Tracking
-This mod adds ability to use 4-point tracking.
-
-# Installation
-* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
-* Get [latest release DLL](../../../releases/latest):
- * Put `ml_fpt.dll` in `Mods` folder of game
-
-# Usage
-* Be sure that your tracker role is set to `Hips` in SteamVR
-* Go to `Settings - Implementation - 4-Point Tracking` and press `Calibrate` button
-* Adjust your tracker in a similar way as in FBT calibration
-* Press trigger on both controllers
-
-# Notes
-* Will be deprecated soon
-* Calibration is saved per avatar for game session.
-* AAS parameters are restored after calibration.
diff --git a/ml_fpt/Scripts.cs b/ml_fpt/Scripts.cs
deleted file mode 100644
index a0b5f81..0000000
--- a/ml_fpt/Scripts.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System;
-using System.IO;
-using System.Reflection;
-
-namespace ml_fpt
-{
- static class Scripts
- {
- public static string GetEmbeddedScript(string p_name)
- {
- string l_result = "";
- Assembly l_assembly = Assembly.GetExecutingAssembly();
- string l_assemblyName = l_assembly.GetName().Name;
-
- try
- {
- Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name);
- StreamReader l_streadReader = new StreamReader(l_libraryStream);
- l_result = l_streadReader.ReadToEnd();
- }
- catch(Exception) { }
-
- return l_result;
- }
- }
-}
diff --git a/ml_fpt/ml_fpt.csproj b/ml_fpt/ml_fpt.csproj
deleted file mode 100644
index 77ebda4..0000000
--- a/ml_fpt/ml_fpt.csproj
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- {EC0A8C41-A429-42CD-B8FA-401A802D4BA6}
- Library
- Properties
- ml_fpt
- ml_fpt
- v4.7.2
- 512
- true
-
-
-
-
-
- true
- bin\x64\Debug\
- DEBUG;TRACE
- full
- x64
- prompt
- MinimumRecommendedRules.ruleset
-
-
- bin\x64\Release\
- TRACE
- true
- pdbonly
- x64
- prompt
- MinimumRecommendedRules.ruleset
-
-
-
- False
-
-
- C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll
- False
-
-
- False
- C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll
- False
-
-
- C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll
- False
-
-
- C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll
- False
-
-
- C:\Games\Steam\common\ChilloutVR\MelonLoader\MelonLoader.dll
- False
-
-
-
-
-
-
-
-
-
- False
-
-
- False
-
-
-
-
-
-
-
-
-
-
-
-
- copy /y "$(TargetPath)" "C:\Games\Steam\common\ChilloutVR\Mods\
-
-
\ No newline at end of file
diff --git a/ml_fpt/ml_fpt.csproj.user b/ml_fpt/ml_fpt.csproj.user
deleted file mode 100644
index 2539084..0000000
--- a/ml_fpt/ml_fpt.csproj.user
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
- C:\Games\Steam\common\ChilloutVR\MelonLoader\;C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\
-
-
\ No newline at end of file
diff --git a/ml_fpt/resources/menu.js b/ml_fpt/resources/menu.js
deleted file mode 100644
index ee02114..0000000
--- a/ml_fpt/resources/menu.js
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- let l_block = document.createElement('div');
- l_block.innerHTML = `
-
-
-
- `;
- document.getElementById('settings-implementation').appendChild(l_block);
-}
\ No newline at end of file
diff --git a/ml_lme/LeapInput.cs b/ml_lme/LeapInput.cs
index cd6414b..f6e9f59 100644
--- a/ml_lme/LeapInput.cs
+++ b/ml_lme/LeapInput.cs
@@ -2,20 +2,19 @@
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
using ABI_RC.Systems.IK;
+using ABI_RC.Systems.InputManagement;
using System.Collections;
-using System.Reflection;
using UnityEngine;
namespace ml_lme
{
- [DisallowMultipleComponent]
class LeapInput : CVRInputModule
{
- CVRInputManager m_inputManager = null;
- InputModuleOpenXR m_openXrModule = null;
bool m_inVR = false;
bool m_gripToGrab = true;
+ bool m_handVisibleLeft = false;
+ bool m_handVisibleRight = false;
ControllerRay m_handRayLeft = null;
ControllerRay m_handRayRight = null;
LineRenderer m_lineLeft = null;
@@ -25,22 +24,31 @@ namespace ml_lme
bool m_gripLeft = false;
bool m_gripRight = false;
- public new void Start()
+ ~LeapInput()
{
- base.Start();
+ Settings.EnabledChange -= this.OnEnableChange;
+ Settings.InputChange -= this.OnInputChange;
+
+ MetaPort.Instance.settings.settingBoolChanged.RemoveListener(this.OnGameSettingBoolChange);
+ }
+
+ public override void ModuleAdded()
+ {
+ base.ModuleAdded();
+
+ InputEnabled = Settings.Enabled;
+ HapticFeedback = false;
- m_inputManager = CVRInputManager.Instance; // _inputManager is stripped out, cool beans
- m_openXrModule = m_inputManager.GetComponent();
m_inVR = Utils.IsInVR();
- m_handRayLeft = LeapTracking.GetInstance().GetLeftHand().gameObject.AddComponent();
+ m_handRayLeft = LeapTracking.Instance.GetLeftHand().gameObject.AddComponent();
m_handRayLeft.hand = true;
m_handRayLeft.generalMask = -1485;
m_handRayLeft.isInteractionRay = true;
m_handRayLeft.triggerGazeEvents = false;
m_handRayLeft.holderRoot = m_handRayLeft.gameObject;
- m_handRayRight = LeapTracking.GetInstance().GetRightHand().gameObject.AddComponent();
+ m_handRayRight = LeapTracking.Instance.GetRightHand().gameObject.AddComponent();
m_handRayRight.hand = false;
m_handRayRight.generalMask = -1485;
m_handRayRight.isInteractionRay = true;
@@ -107,43 +115,64 @@ namespace ml_lme
m_lineRight.gameObject.layer = PlayerSetup.Instance.leftRay.gameObject.layer;
}
- void OnDestroy()
- {
- Settings.EnabledChange -= this.OnEnableChange;
- Settings.InputChange -= this.OnInputChange;
- }
-
- void Update()
- {
- GestureMatcher.LeapData l_data = LeapManager.GetInstance().GetLatestData();
-
- if(Settings.Enabled)
- {
- if(l_data.m_leftHand.m_present)
- SetFingersInput(l_data.m_leftHand, true);
-
- if(l_data.m_rightHand.m_present)
- SetFingersInput(l_data.m_rightHand, false);
-
- if(m_inVR)
- {
- m_inputManager.individualFingerTracking = !m_openXrModule.GetIndexGestureToggle();
- m_inputManager.individualFingerTracking |= (l_data.m_leftHand.m_present || l_data.m_rightHand.m_present);
- }
- else
- m_inputManager.individualFingerTracking = (l_data.m_leftHand.m_present || l_data.m_rightHand.m_present);
- IKSystem.Instance.FingerSystem.controlActive = m_inputManager.individualFingerTracking;
- }
-
- m_handRayLeft.enabled = (l_data.m_leftHand.m_present && (!m_inVR || !Utils.IsLeftHandTracked() || !Settings.FingersOnly));
- m_handRayRight.enabled = (l_data.m_rightHand.m_present && (!m_inVR || !Utils.IsRightHandTracked() || !Settings.FingersOnly));
- }
-
public override void UpdateInput()
{
- if(Settings.Enabled && Settings.Input)
+ if(InputEnabled)
{
- GestureMatcher.LeapData l_data = LeapManager.GetInstance().GetLatestData();
+ GestureMatcher.LeapData l_data = LeapManager.Instance.GetLatestData();
+
+ if(l_data.m_leftHand.m_present)
+ {
+ SetFingersInput(l_data.m_leftHand, true);
+ m_handVisibleLeft = true;
+ }
+ else
+ {
+ if(m_handVisibleLeft)
+ {
+ ResetFingers(true);
+ m_handVisibleLeft = false;
+ }
+ }
+
+ if(l_data.m_rightHand.m_present)
+ {
+ SetFingersInput(l_data.m_rightHand, false);
+ m_handVisibleRight = true;
+ }
+ else
+ {
+ if(m_handVisibleRight)
+ {
+ ResetFingers(false);
+ m_handVisibleRight = false;
+ }
+ }
+
+ if(!ModSupporter.SkipFingersOverride())
+ {
+ if(m_inVR)
+ {
+ _inputManager.individualFingerTracking = !CVRInputManager._moduleXR.GestureToggleValue;
+ _inputManager.individualFingerTracking |= (l_data.m_leftHand.m_present || l_data.m_rightHand.m_present);
+ }
+ else
+ _inputManager.individualFingerTracking = (l_data.m_leftHand.m_present || l_data.m_rightHand.m_present);
+ IKSystem.Instance.FingerSystem.controlActive = _inputManager.individualFingerTracking;
+ }
+
+ m_handRayLeft.enabled = (l_data.m_leftHand.m_present && (!m_inVR || !Utils.IsLeftHandTracked() || !Settings.FingersOnly));
+ m_handRayRight.enabled = (l_data.m_rightHand.m_present && (!m_inVR || !Utils.IsRightHandTracked() || !Settings.FingersOnly));
+
+ base.UpdateInput();
+ }
+ }
+
+ public override void Update_Interaction()
+ {
+ if(Settings.Input)
+ {
+ GestureMatcher.LeapData l_data = LeapManager.Instance.GetLatestData();
if(l_data.m_leftHand.m_present && (!m_inVR || !Utils.IsLeftHandTracked() || !Settings.FingersOnly))
{
@@ -154,22 +183,22 @@ namespace ml_lme
l_interactValue = Mathf.Clamp01(Mathf.InverseLerp(Mathf.Min(Settings.GripThreadhold, Settings.InteractThreadhold), Mathf.Max(Settings.GripThreadhold, Settings.InteractThreadhold), l_strength));
else
l_interactValue = Mathf.Clamp01(Mathf.InverseLerp(0f, Settings.InteractThreadhold, l_strength));
- m_inputManager.interactLeftValue = Mathf.Max(l_interactValue, m_inputManager.interactLeftValue);
+ _inputManager.interactLeftValue = Mathf.Max(l_interactValue, _inputManager.interactLeftValue);
if(m_interactLeft != (l_strength > Settings.InteractThreadhold))
{
m_interactLeft = (l_strength > Settings.InteractThreadhold);
- m_inputManager.interactLeftDown |= m_interactLeft;
- m_inputManager.interactLeftUp |= !m_interactLeft;
+ _inputManager.interactLeftDown |= m_interactLeft;
+ _inputManager.interactLeftUp |= !m_interactLeft;
}
float l_gripValue = Mathf.Clamp01(Mathf.InverseLerp(0f, Settings.GripThreadhold, l_strength));
- m_inputManager.gripLeftValue = Mathf.Max(l_gripValue, m_inputManager.gripLeftValue);
+ _inputManager.gripLeftValue = Mathf.Max(l_gripValue, _inputManager.gripLeftValue);
if(m_gripLeft != (l_strength > Settings.GripThreadhold))
{
m_gripLeft = (l_strength > Settings.GripThreadhold);
- m_inputManager.gripLeftDown |= m_gripLeft;
- m_inputManager.gripLeftUp |= !m_gripLeft;
+ _inputManager.gripLeftDown |= m_gripLeft;
+ _inputManager.gripLeftUp |= !m_gripLeft;
}
}
@@ -182,22 +211,22 @@ namespace ml_lme
l_interactValue = Mathf.Clamp01(Mathf.InverseLerp(Mathf.Min(Settings.GripThreadhold, Settings.InteractThreadhold), Mathf.Max(Settings.GripThreadhold, Settings.InteractThreadhold), l_strength));
else
l_interactValue = Mathf.Clamp01(Mathf.InverseLerp(0f, Settings.InteractThreadhold, l_strength));
- m_inputManager.interactRightValue = Mathf.Max(l_interactValue, m_inputManager.interactRightValue);
+ _inputManager.interactRightValue = Mathf.Max(l_interactValue, _inputManager.interactRightValue);
if(m_interactRight != (l_strength > Settings.InteractThreadhold))
{
m_interactRight = (l_strength > Settings.InteractThreadhold);
- m_inputManager.interactRightDown |= m_interactRight;
- m_inputManager.interactRightUp |= !m_interactRight;
+ _inputManager.interactRightDown |= m_interactRight;
+ _inputManager.interactRightUp |= !m_interactRight;
}
float l_gripValue = Mathf.Clamp01(Mathf.InverseLerp(0f, Settings.GripThreadhold, l_strength));
- m_inputManager.gripRightValue = Mathf.Max(l_gripValue, m_inputManager.gripRightValue);
+ _inputManager.gripRightValue = Mathf.Max(l_gripValue, _inputManager.gripRightValue);
if(m_gripRight != (l_strength > Settings.GripThreadhold))
{
m_gripRight = (l_strength > Settings.GripThreadhold);
- m_inputManager.gripRightDown |= m_gripRight;
- m_inputManager.gripRightUp |= !m_gripRight;
+ _inputManager.gripRightDown |= m_gripRight;
+ _inputManager.gripRightUp |= !m_gripRight;
}
}
}
@@ -206,8 +235,12 @@ namespace ml_lme
// Settings changes
void OnEnableChange(bool p_state)
{
+ InputEnabled = p_state;
+
OnInputChange(p_state && Settings.Input);
UpdateFingerTracking();
+ m_handVisibleLeft &= p_state;
+ m_handVisibleRight &= p_state;
}
void OnInputChange(bool p_state)
@@ -248,27 +281,73 @@ namespace ml_lme
// Arbitrary
void UpdateFingerTracking()
{
- m_inputManager.individualFingerTracking = (Settings.Enabled || (m_inVR && m_openXrModule.AreKnucklesInUse() && !m_openXrModule.GetIndexGestureToggle()));
- IKSystem.Instance.FingerSystem.controlActive = m_inputManager.individualFingerTracking;
+ _inputManager.individualFingerTracking = (Settings.Enabled || (m_inVR && Utils.AreKnucklesInUse() && !CVRInputManager._moduleXR.GestureToggleValue));
+ IKSystem.Instance.FingerSystem.controlActive = _inputManager.individualFingerTracking;
+
+ if(!Settings.Enabled)
+ {
+ ResetFingers(true);
+ ResetFingers(false);
+ }
}
void SetFingersInput(GestureMatcher.HandData p_hand, bool p_left)
{
if(p_left)
{
- m_inputManager.fingerCurlLeftThumb = p_hand.m_bends[0];
- m_inputManager.fingerCurlLeftIndex = p_hand.m_bends[1];
- m_inputManager.fingerCurlLeftMiddle = p_hand.m_bends[2];
- m_inputManager.fingerCurlLeftRing = p_hand.m_bends[3];
- m_inputManager.fingerCurlLeftPinky = p_hand.m_bends[4];
+ _inputManager.fingerCurlLeftThumb = p_hand.m_bends[0];
+ _inputManager.fingerCurlLeftIndex = p_hand.m_bends[1];
+ _inputManager.fingerCurlLeftMiddle = p_hand.m_bends[2];
+ _inputManager.fingerCurlLeftRing = p_hand.m_bends[3];
+ _inputManager.fingerCurlLeftPinky = p_hand.m_bends[4];
+ _inputManager.fingerSpreadLeftThumb = p_hand.m_spreads[0];
+ _inputManager.fingerSpreadLeftIndex = p_hand.m_spreads[1];
+ _inputManager.fingerSpreadLeftMiddle = p_hand.m_spreads[2];
+ _inputManager.fingerSpreadLeftRing = p_hand.m_spreads[3];
+ _inputManager.fingerSpreadLeftPinky = p_hand.m_spreads[4];
}
else
{
- m_inputManager.fingerCurlRightThumb = p_hand.m_bends[0];
- m_inputManager.fingerCurlRightIndex = p_hand.m_bends[1];
- m_inputManager.fingerCurlRightMiddle = p_hand.m_bends[2];
- m_inputManager.fingerCurlRightRing = p_hand.m_bends[3];
- m_inputManager.fingerCurlRightPinky = p_hand.m_bends[4];
+ _inputManager.fingerCurlRightThumb = p_hand.m_bends[0];
+ _inputManager.fingerCurlRightIndex = p_hand.m_bends[1];
+ _inputManager.fingerCurlRightMiddle = p_hand.m_bends[2];
+ _inputManager.fingerCurlRightRing = p_hand.m_bends[3];
+ _inputManager.fingerCurlRightPinky = p_hand.m_bends[4];
+ _inputManager.fingerSpreadRightThumb = p_hand.m_spreads[0];
+ _inputManager.fingerSpreadRightIndex = p_hand.m_spreads[1];
+ _inputManager.fingerSpreadRightMiddle = p_hand.m_spreads[2];
+ _inputManager.fingerSpreadRightRing = p_hand.m_spreads[3];
+ _inputManager.fingerSpreadRightPinky = p_hand.m_spreads[4];
+ }
+ }
+
+ void ResetFingers(bool p_left)
+ {
+ if(p_left)
+ {
+ _inputManager.fingerCurlLeftThumb = 0f;
+ _inputManager.fingerCurlLeftIndex = 0f;
+ _inputManager.fingerCurlLeftMiddle = 0f;
+ _inputManager.fingerCurlLeftRing = 0f;
+ _inputManager.fingerCurlLeftPinky = 0f;
+ _inputManager.fingerSpreadLeftThumb = 0f;
+ _inputManager.fingerSpreadLeftIndex = 0f;
+ _inputManager.fingerSpreadLeftMiddle = 0f;
+ _inputManager.fingerSpreadLeftRing = 0f;
+ _inputManager.fingerSpreadLeftPinky = 0f;
+ }
+ else
+ {
+ _inputManager.fingerCurlRightThumb = 0f;
+ _inputManager.fingerCurlRightIndex = 0f;
+ _inputManager.fingerCurlRightMiddle = 0f;
+ _inputManager.fingerCurlRightRing = 0f;
+ _inputManager.fingerCurlRightPinky = 0f;
+ _inputManager.fingerSpreadRightThumb = 0f;
+ _inputManager.fingerSpreadRightIndex = 0f;
+ _inputManager.fingerSpreadRightMiddle = 0f;
+ _inputManager.fingerSpreadRightRing = 0f;
+ _inputManager.fingerSpreadRightPinky = 0f;
}
}
diff --git a/ml_lme/LeapManager.cs b/ml_lme/LeapManager.cs
index 1edaa21..2819eb7 100644
--- a/ml_lme/LeapManager.cs
+++ b/ml_lme/LeapManager.cs
@@ -1,5 +1,6 @@
using ABI_RC.Core.Player;
using ABI_RC.Core.Savior;
+using ABI_RC.Systems.InputManagement;
using System.Collections;
using UnityEngine;
@@ -8,32 +9,22 @@ namespace ml_lme
[DisallowMultipleComponent]
class LeapManager : MonoBehaviour
{
- static LeapManager ms_instance = null;
+ public static LeapManager Instance { get; private set; } = null;
- readonly Leap.Controller m_leapController = null;
- readonly GestureMatcher.LeapData m_leapData = null;
+ Leap.Controller m_leapController = null;
+ GestureMatcher.LeapData m_leapData = null;
LeapTracking m_leapTracking = null;
LeapTracked m_leapTracked = null;
LeapInput m_leapInput = null;
- public static LeapManager GetInstance() => ms_instance;
-
- internal LeapManager()
+ void Awake()
{
+ if(Instance == null)
+ Instance = this;
+
m_leapController = new Leap.Controller();
m_leapData = new GestureMatcher.LeapData();
- }
- ~LeapManager()
- {
- m_leapController.StopConnection();
- m_leapController.Dispose();
- }
-
- void Start()
- {
- if(ms_instance == null)
- ms_instance = this;
DontDestroyOnLoad(this);
@@ -49,41 +40,44 @@ namespace ml_lme
m_leapTracking = new GameObject("[LeapTrackingRoot]").AddComponent();
m_leapTracking.transform.parent = this.transform;
- MelonLoader.MelonCoroutines.Start(WaitForInputManager());
- MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
-
OnEnableChange(Settings.Enabled);
OnTrackingModeChange(Settings.TrackingMode);
+
+ MelonLoader.MelonCoroutines.Start(WaitForObjects());
}
void OnDestroy()
{
- if(ms_instance == this)
- ms_instance = null;
+ if(Instance == this)
+ Instance = null;
+ m_leapController.StopConnection();
m_leapController.Device -= this.OnLeapDeviceInitialized;
m_leapController.DeviceFailure -= this.OnLeapDeviceFailure;
m_leapController.DeviceLost -= this.OnLeapDeviceLost;
m_leapController.Connect -= this.OnLeapServiceConnect;
m_leapController.Disconnect -= this.OnLeapServiceDisconnect;
+ m_leapController.Dispose();
+ m_leapController = null;
Settings.EnabledChange -= this.OnEnableChange;
Settings.TrackingModeChange -= this.OnTrackingModeChange;
}
- IEnumerator WaitForInputManager()
+ IEnumerator WaitForObjects()
{
while(CVRInputManager.Instance == null)
yield return null;
- m_leapInput = CVRInputManager.Instance.gameObject.AddComponent();
- }
-
- IEnumerator WaitForLocalPlayer()
- {
while(PlayerSetup.Instance == null)
yield return null;
+ while(LeapTracking.Instance == null)
+ yield return null;
+
+ m_leapInput = new LeapInput();
+ CVRInputManager.Instance.AddInputModule(m_leapInput);
+
m_leapTracked = PlayerSetup.Instance.gameObject.AddComponent();
}
@@ -168,8 +162,9 @@ namespace ml_lme
{
if(m_leapTracking != null)
m_leapTracking.OnAvatarSetup();
- if(m_leapInput != null)
- m_leapInput.OnAvatarSetup();
+
+ m_leapInput?.OnAvatarSetup();
+
if(m_leapTracked != null)
m_leapTracked.OnAvatarSetup();
}
@@ -182,8 +177,7 @@ namespace ml_lme
internal void OnRayScale(float p_scale)
{
- if(m_leapInput != null)
- m_leapInput.OnRayScale(p_scale);
+ m_leapInput?.OnRayScale(p_scale);
}
internal void OnPlayspaceScale(float p_relation)
diff --git a/ml_lme/LeapTracked.cs b/ml_lme/LeapTracked.cs
index 7c8cfea..fe0b76a 100644
--- a/ml_lme/LeapTracked.cs
+++ b/ml_lme/LeapTracked.cs
@@ -43,12 +43,12 @@ namespace ml_lme
m_inVR = Utils.IsInVR();
m_leftHandTarget = new GameObject("RotationTarget").transform;
- m_leftHandTarget.parent = LeapTracking.GetInstance().GetLeftHand();
+ m_leftHandTarget.parent = LeapTracking.Instance.GetLeftHand();
m_leftHandTarget.localPosition = Vector3.zero;
m_leftHandTarget.localRotation = Quaternion.identity;
m_rightHandTarget = new GameObject("RotationTarget").transform;
- m_rightHandTarget.parent = LeapTracking.GetInstance().GetRightHand();
+ m_rightHandTarget.parent = LeapTracking.Instance.GetRightHand();
m_rightHandTarget.localPosition = Vector3.zero;
m_rightHandTarget.localRotation = Quaternion.identity;
@@ -72,7 +72,7 @@ namespace ml_lme
{
if(m_enabled)
{
- GestureMatcher.LeapData l_data = LeapManager.GetInstance().GetLatestData();
+ GestureMatcher.LeapData l_data = LeapManager.Instance.GetLatestData();
if((m_leftArmIK != null) && (m_rightArmIK != null))
{
@@ -92,7 +92,7 @@ namespace ml_lme
if(l_data.m_leftHand.m_present && !m_leftTargetActive)
{
m_vrIK.solver.leftArm.target = m_leftHandTarget;
- m_vrIK.solver.leftArm.bendGoal = LeapTracking.GetInstance().GetLeftElbow();
+ m_vrIK.solver.leftArm.bendGoal = LeapTracking.Instance.GetLeftElbow();
m_vrIK.solver.leftArm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_leftTargetActive = true;
}
@@ -107,7 +107,7 @@ namespace ml_lme
if(l_data.m_rightHand.m_present && !m_rightTargetActive)
{
m_vrIK.solver.rightArm.target = m_rightHandTarget;
- m_vrIK.solver.rightArm.bendGoal = LeapTracking.GetInstance().GetRightElbow();
+ m_vrIK.solver.rightArm.bendGoal = LeapTracking.Instance.GetRightElbow();
m_vrIK.solver.rightArm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_rightTargetActive = true;
}
@@ -126,7 +126,7 @@ namespace ml_lme
{
if(m_enabled && !m_inVR && (m_poseHandler != null))
{
- GestureMatcher.LeapData l_data = LeapManager.GetInstance().GetLatestData();
+ GestureMatcher.LeapData l_data = LeapManager.Instance.GetLatestData();
Vector3 l_hipsLocalPos = m_hips.localPosition;
Quaternion l_hipsLocalRot = m_hips.localRotation;
@@ -232,7 +232,10 @@ namespace ml_lme
if(PlayerSetup.Instance._animator.isHuman)
{
+ Vector3 l_hipsPos = Vector3.zero;
m_hips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips);
+ if(m_hips != null)
+ l_hipsPos = m_hips.localPosition;
if(!m_inVR)
{
@@ -278,7 +281,7 @@ namespace ml_lme
PlayerSetup.Instance._animator.transform
);
m_leftArmIK.solver.arm.target = m_leftHandTarget;
- m_leftArmIK.solver.arm.bendGoal = LeapTracking.GetInstance().GetLeftElbow();
+ m_leftArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetLeftElbow();
m_leftArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_leftArmIK.enabled = (m_enabled && !m_fingersOnly);
@@ -293,7 +296,7 @@ namespace ml_lme
PlayerSetup.Instance._animator.transform
);
m_rightArmIK.solver.arm.target = m_rightHandTarget;
- m_rightArmIK.solver.arm.bendGoal = LeapTracking.GetInstance().GetRightElbow();
+ m_rightArmIK.solver.arm.bendGoal = LeapTracking.Instance.GetRightElbow();
m_rightArmIK.solver.arm.bendGoalWeight = (m_trackElbows ? 1f : 0f);
m_rightArmIK.enabled = (m_enabled && !m_fingersOnly);
@@ -308,6 +311,9 @@ namespace ml_lme
m_vrIK.solver.OnPreUpdate += this.OnIKPreUpdate;
m_vrIK.solver.OnPostUpdate += this.OnIKPostUpdate;
}
+
+ if(m_hips != null)
+ m_hips.localPosition = l_hipsPos;
}
}
diff --git a/ml_lme/LeapTracking.cs b/ml_lme/LeapTracking.cs
index 251b9cd..429b904 100644
--- a/ml_lme/LeapTracking.cs
+++ b/ml_lme/LeapTracking.cs
@@ -7,7 +7,7 @@ namespace ml_lme
[DisallowMultipleComponent]
class LeapTracking : MonoBehaviour
{
- static LeapTracking ms_instance = null;
+ public static LeapTracking Instance { get; private set; } = null;
static Quaternion ms_identityRotation = Quaternion.identity;
bool m_inVR = false;
@@ -20,12 +20,10 @@ namespace ml_lme
float m_scaleRelation = 1f;
- public static LeapTracking GetInstance() => ms_instance;
-
void Start()
{
- if(ms_instance == null)
- ms_instance = this;
+ if(Instance == null)
+ Instance = this;
m_inVR = Utils.IsInVR();
@@ -82,8 +80,8 @@ namespace ml_lme
void OnDestroy()
{
- if(ms_instance == this)
- ms_instance = null;
+ if(Instance == this)
+ Instance = null;
Settings.DesktopOffsetChange -= this.OnDesktopOffsetChange;
Settings.ModelVisibilityChange -= this.OnModelVisibilityChange;
@@ -97,7 +95,7 @@ namespace ml_lme
{
if(Settings.Enabled)
{
- GestureMatcher.LeapData l_data = LeapManager.GetInstance().GetLatestData();
+ GestureMatcher.LeapData l_data = LeapManager.Instance.GetLatestData();
if(l_data.m_leftHand.m_present)
{
diff --git a/ml_lme/Main.cs b/ml_lme/Main.cs
index 2bfb331..a2c9635 100644
--- a/ml_lme/Main.cs
+++ b/ml_lme/Main.cs
@@ -49,6 +49,7 @@ namespace ml_lme
new HarmonyLib.HarmonyMethod(typeof(LeapMotionExtension).GetMethod(nameof(OnPlayspaceScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
);
+ ModSupporter.Init();
MelonLoader.MelonCoroutines.Start(WaitForRootLogic());
}
diff --git a/ml_lme/ModSupporter.cs b/ml_lme/ModSupporter.cs
new file mode 100644
index 0000000..9747c8d
--- /dev/null
+++ b/ml_lme/ModSupporter.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ml_lme
+{
+ static class ModSupporter
+ {
+ static bool ms_copycatMod = false;
+
+ public static void Init()
+ {
+ if(MelonLoader.MelonMod.RegisteredMelons.FirstOrDefault(m => m.Info.Name == "PlayerMovementCopycat") != null)
+ MelonLoader.MelonCoroutines.Start(WaitForCopycatInstance());
+ }
+
+ // PlayerMovementCopycat support
+ static IEnumerator WaitForCopycatInstance()
+ {
+ while(ml_pmc.PoseCopycat.Instance == null)
+ yield return null;
+
+ ms_copycatMod = true;
+ }
+ static bool IsCopycating() => (ml_pmc.PoseCopycat.Instance.IsActive() && ml_pmc.PoseCopycat.Instance.IsFingerTrackingActive());
+
+ public static bool SkipFingersOverride()
+ {
+ bool l_result = false;
+ l_result |= (ms_copycatMod && IsCopycating());
+ return l_result;
+ }
+ }
+}
diff --git a/ml_lme/Properties/AssemblyInfo.cs b/ml_lme/Properties/AssemblyInfo.cs
index 6599485..7352871 100644
--- a/ml_lme/Properties/AssemblyInfo.cs
+++ b/ml_lme/Properties/AssemblyInfo.cs
@@ -1,4 +1,7 @@
-[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.3.2-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
+using System.Reflection;
+
+[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.3.7", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
+[assembly: MelonLoader.MelonOptionalDependencies("ml_pmc")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
diff --git a/ml_lme/Settings.cs b/ml_lme/Settings.cs
index b1c04c6..56ff521 100644
--- a/ml_lme/Settings.cs
+++ b/ml_lme/Settings.cs
@@ -1,4 +1,5 @@
using ABI_RC.Core.InteractionSystem;
+using cohtml;
using System;
using System.Collections.Generic;
using UnityEngine;
@@ -67,7 +68,7 @@ namespace ml_lme
internal static void Init()
{
- ms_category = MelonLoader.MelonPreferences.CreateCategory("LME");
+ ms_category = MelonLoader.MelonPreferences.CreateCategory("LME", null, true);
ms_entries = new List()
{
diff --git a/ml_lme/Utils.cs b/ml_lme/Utils.cs
index b2eed59..455a165 100644
--- a/ml_lme/Utils.cs
+++ b/ml_lme/Utils.cs
@@ -1,8 +1,10 @@
-using ABI_RC.Core.Savior;
+using ABI_RC.Core.Player;
+using ABI_RC.Core.Savior;
using ABI_RC.Core.UI;
+using ABI_RC.Systems.InputManagement;
+using System.Linq;
using System.Reflection;
using UnityEngine;
-using UnityEngine.XR;
namespace ml_lme
{
@@ -10,25 +12,19 @@ namespace ml_lme
{
static readonly Quaternion ms_hmdRotationFix = new Quaternion(0f, 0.7071068f, 0.7071068f, 0f);
static readonly Quaternion ms_screentopRotationFix = new Quaternion(0f, 0f, -1f, 0f);
- static readonly FieldInfo ms_leftControllerName = typeof(InputModuleOpenXR).GetField("_leftHandControllerName", BindingFlags.NonPublic | BindingFlags.Instance);
- static readonly FieldInfo ms_rightControllerName = typeof(InputModuleOpenXR).GetField("_rightHandControllerName", BindingFlags.NonPublic | BindingFlags.Instance);
- static readonly FieldInfo ms_indexGestureToggle = typeof(InputModuleOpenXR).GetField("_steamVrIndexGestureToggleValue", BindingFlags.Instance | BindingFlags.NonPublic);
+
static FieldInfo ms_cohtmlView = typeof(CohtmlControlledViewDisposable).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance);
public static bool IsInVR() => ((CheckVR.Instance != null) && CheckVR.Instance.hasVrDeviceLoaded);
- public static bool AreKnucklesInUse(this InputModuleOpenXR p_module) => (((string)ms_leftControllerName.GetValue(p_module)).Contains("Index") || ((string)ms_rightControllerName.GetValue(p_module)).Contains("Index"));
- public static bool GetIndexGestureToggle(this InputModuleOpenXR p_module) => (bool)ms_indexGestureToggle.GetValue(p_module);
- public static bool IsLeftHandTracked() => InputDevices.GetDeviceAtXRNode(XRNode.LeftHand).isValid;
- public static bool IsRightHandTracked() => InputDevices.GetDeviceAtXRNode(XRNode.RightHand).isValid;
-
+ public static bool AreKnucklesInUse() => ((CVRInputManager.Instance._leftController == ABI_RC.Systems.InputManagement.XR.EXRControllerType.Index) || (CVRInputManager.Instance._rightController == ABI_RC.Systems.InputManagement.XR.EXRControllerType.Index));
+ public static bool IsLeftHandTracked() => (CVRInputManager.Instance._leftController != ABI_RC.Systems.InputManagement.XR.EXRControllerType.None);
+ public static bool IsRightHandTracked() => (CVRInputManager.Instance._rightController != ABI_RC.Systems.InputManagement.XR.EXRControllerType.None);
public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false)
{
return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.lossyScale : Vector3.one);
}
- public static void ExecuteScript(this CohtmlControlledViewDisposable p_viewDisposable, string p_script) => ((cohtml.Net.View)ms_cohtmlView.GetValue(p_viewDisposable))?.ExecuteScript(p_script);
-
public static void ShowHUDNotification(string p_title, string p_message, string p_small = "", bool p_immediate = false)
{
if(CohtmlHud.Instance != null)
@@ -40,6 +36,8 @@ namespace ml_lme
}
}
+ public static void ExecuteScript(this CohtmlControlledViewDisposable p_instance, string p_script) => ((cohtml.Net.View)ms_cohtmlView.GetValue(p_instance))?.ExecuteScript(p_script);
+
public static void LeapToUnity(ref Vector3 p_pos, ref Quaternion p_rot, Settings.LeapTrackingMode p_mode)
{
p_pos *= 0.001f;
@@ -50,20 +48,20 @@ namespace ml_lme
switch(p_mode)
{
case Settings.LeapTrackingMode.Screentop:
- {
- p_pos.x *= -1f;
- p_pos.y *= -1f;
- p_rot = (ms_screentopRotationFix * p_rot);
- }
- break;
+ {
+ p_pos.x *= -1f;
+ p_pos.y *= -1f;
+ p_rot = (ms_screentopRotationFix * p_rot);
+ }
+ break;
case Settings.LeapTrackingMode.HMD:
- {
- p_pos.x *= -1f;
- Swap(ref p_pos.y, ref p_pos.z);
- p_rot = (ms_hmdRotationFix * p_rot);
- }
- break;
+ {
+ p_pos.x *= -1f;
+ Swap(ref p_pos.y, ref p_pos.z);
+ p_rot = (ms_hmdRotationFix * p_rot);
+ }
+ break;
}
}
diff --git a/ml_lme/ml_lme.csproj b/ml_lme/ml_lme.csproj
index bd5702a..ff9c655 100644
--- a/ml_lme/ml_lme.csproj
+++ b/ml_lme/ml_lme.csproj
@@ -4,7 +4,7 @@
netstandard2.1
x64
LeapMotionExtension
- 1.3.2
+ 1.3.7
SDraw
None
LeapMotionExtension
@@ -37,8 +37,7 @@
- D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\0Harmony.dll
- false
+ D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll
D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll
@@ -57,8 +56,10 @@
false
- D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\MelonLoader.dll
- false
+ D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll
+
+
+ D:\games\Steam\steamapps\common\ChilloutVR\Mods\ml_pmc.dll
D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll
diff --git a/ml_mods_cvr.sln b/ml_mods_cvr.sln
index ecbbad0..9a06e49 100644
--- a/ml_mods_cvr.sln
+++ b/ml_mods_cvr.sln
@@ -15,6 +15,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_lme", "ml_lme\ml_lme.csp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ml_pam", "ml_pam\ml_pam.csproj", "{5B614459-234A-443D-B06D-34FF81ADA67E}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ml_prm", "ml_prm\ml_prm.csproj", "{D27B6D36-884F-4A49-9A25-B9C121E7B65F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ml_pmc", "ml_pmc\ml_pmc.csproj", "{118675AA-9AC7-4B0C-BFB1-FA1691619502}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -39,6 +43,12 @@ Global
{5B614459-234A-443D-B06D-34FF81ADA67E}.Debug|x64.ActiveCfg = Debug|x64
{5B614459-234A-443D-B06D-34FF81ADA67E}.Release|x64.ActiveCfg = Release|x64
{5B614459-234A-443D-B06D-34FF81ADA67E}.Release|x64.Build.0 = Release|x64
+ {D27B6D36-884F-4A49-9A25-B9C121E7B65F}.Debug|x64.ActiveCfg = Debug|x64
+ {D27B6D36-884F-4A49-9A25-B9C121E7B65F}.Release|x64.ActiveCfg = Release|x64
+ {D27B6D36-884F-4A49-9A25-B9C121E7B65F}.Release|x64.Build.0 = Release|x64
+ {118675AA-9AC7-4B0C-BFB1-FA1691619502}.Debug|x64.ActiveCfg = Debug|x64
+ {118675AA-9AC7-4B0C-BFB1-FA1691619502}.Release|x64.ActiveCfg = Release|x64
+ {118675AA-9AC7-4B0C-BFB1-FA1691619502}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/ml_pam/ArmMover.cs b/ml_pam/ArmMover.cs
index c9940ae..6091e3b 100644
--- a/ml_pam/ArmMover.cs
+++ b/ml_pam/ArmMover.cs
@@ -132,6 +132,11 @@ namespace ml_pam
if(PlayerSetup.Instance._animator.isHuman)
{
+ Vector3 l_hipsPos = Vector3.zero;
+ Transform l_hips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips);
+ if(l_hips != null)
+ l_hipsPos = l_hips.localPosition;
+
HumanPose l_currentPose = new HumanPose();
HumanPoseHandler l_poseHandler = null;
@@ -190,6 +195,9 @@ namespace ml_pam
l_poseHandler?.SetHumanPose(ref l_currentPose);
l_poseHandler?.Dispose();
+
+ if(l_hips != null)
+ l_hips.localPosition = l_hipsPos;
}
if(m_enabled)
diff --git a/ml_pam/Properties/AssemblyInfo.cs b/ml_pam/Properties/AssemblyInfo.cs
index 4e400ae..fd2a783 100644
--- a/ml_pam/Properties/AssemblyInfo.cs
+++ b/ml_pam/Properties/AssemblyInfo.cs
@@ -1,4 +1,6 @@
-[assembly: MelonLoader.MelonInfo(typeof(ml_pam.PickupArmMovement), "PickupArmMovement", "1.0.2-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
+using System.Reflection;
+
+[assembly: MelonLoader.MelonInfo(typeof(ml_pam.PickupArmMovement), "PickupArmMovement", "1.0.5", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPriority(1)]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
diff --git a/ml_pam/Settings.cs b/ml_pam/Settings.cs
index 92ac61a..433cdab 100644
--- a/ml_pam/Settings.cs
+++ b/ml_pam/Settings.cs
@@ -1,4 +1,5 @@
using ABI_RC.Core.InteractionSystem;
+using cohtml;
using System;
using System.Collections.Generic;
@@ -23,7 +24,7 @@ namespace ml_pam
internal static void Init()
{
- ms_category = MelonLoader.MelonPreferences.CreateCategory("PAM");
+ ms_category = MelonLoader.MelonPreferences.CreateCategory("PAM", null, true);
ms_entries = new List()
{
diff --git a/ml_pam/Utils.cs b/ml_pam/Utils.cs
index 9bed194..fc1a2ab 100644
--- a/ml_pam/Utils.cs
+++ b/ml_pam/Utils.cs
@@ -10,12 +10,12 @@ namespace ml_pam
public static bool IsInVR() => ((ABI_RC.Core.Savior.CheckVR.Instance != null) && ABI_RC.Core.Savior.CheckVR.Instance.hasVrDeviceLoaded);
+ public static void ExecuteScript(this CohtmlControlledViewDisposable p_instance, string p_script) => ((cohtml.Net.View)ms_cohtmlView.GetValue(p_instance))?.ExecuteScript(p_script);
+
// Extensions
public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false)
{
return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.localScale : Vector3.one);
}
-
- public static void ExecuteScript(this CohtmlControlledViewDisposable p_viewDisposable, string p_script) => ((cohtml.Net.View)ms_cohtmlView.GetValue(p_viewDisposable))?.ExecuteScript(p_script);
}
}
diff --git a/ml_pam/ml_pam.csproj b/ml_pam/ml_pam.csproj
index 83de615..51dd025 100644
--- a/ml_pam/ml_pam.csproj
+++ b/ml_pam/ml_pam.csproj
@@ -4,7 +4,7 @@
netstandard2.1
x64
PickupArmMovement
- 1.0.2
+ 1.0.5
SDraw
None
PickupArmMovement
@@ -26,8 +26,7 @@
- D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\0Harmony.dll
- false
+ D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll
D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll
@@ -46,8 +45,7 @@
false
- D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\MelonLoader.dll
- false
+ D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll
D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll
diff --git a/ml_pmc/Main.cs b/ml_pmc/Main.cs
new file mode 100644
index 0000000..bb3a3ea
--- /dev/null
+++ b/ml_pmc/Main.cs
@@ -0,0 +1,130 @@
+using ABI_RC.Core.Networking.IO.Social;
+using ABI_RC.Core.Player;
+using ABI_RC.Systems.MovementSystem;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using UnityEngine;
+
+namespace ml_pmc
+{
+ public class PlayerMovementCopycat : MelonLoader.MelonMod
+ {
+ static PlayerMovementCopycat ms_instance = null;
+
+ PoseCopycat m_localCopycat = null;
+
+ public override void OnInitializeMelon()
+ {
+ if(ms_instance == null)
+ ms_instance = this;
+
+ Settings.Init();
+ ModUi.Init();
+
+ HarmonyInstance.Patch(
+ typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)),
+ null,
+ new HarmonyLib.HarmonyMethod(typeof(PlayerMovementCopycat).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
+ );
+ HarmonyInstance.Patch(
+ typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)),
+ null,
+ new HarmonyLib.HarmonyMethod(typeof(PlayerMovementCopycat).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
+ );
+
+ MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
+ }
+
+ public override void OnDeinitializeMelon()
+ {
+ if(ms_instance == this)
+ ms_instance = null;
+
+ m_localCopycat = null;
+ }
+
+ System.Collections.IEnumerator WaitForLocalPlayer()
+ {
+ while(PlayerSetup.Instance == null)
+ yield return null;
+
+ m_localCopycat = PlayerSetup.Instance.gameObject.AddComponent();
+ ModUi.CopySwitch += this.OnTargetSelect;
+ }
+
+ void OnTargetSelect(string p_id)
+ {
+ if(m_localCopycat != null)
+ {
+ if(m_localCopycat.IsActive())
+ m_localCopycat.SetTarget(null);
+ else
+ {
+ if(Friends.FriendsWith(p_id))
+ {
+ if(CVRPlayerManager.Instance.GetPlayerPuppetMaster(p_id, out PuppetMaster l_puppetMaster))
+ {
+ if(IsInSight(MovementSystem.Instance.proxyCollider, l_puppetMaster.GetComponent(), Utils.GetWorldMovementLimit()))
+ m_localCopycat.SetTarget(l_puppetMaster.gameObject);
+ else
+ ModUi.ShowAlert("Selected player is too far away or obstructed");
+ }
+ else
+ ModUi.ShowAlert("Selected player isn't connected or ready yet");
+ }
+ else
+ ModUi.ShowAlert("Selected player isn't your friend");
+ }
+ }
+ }
+
+ static bool IsInSight(CapsuleCollider p_source, CapsuleCollider p_target, float p_limit)
+ {
+ bool l_result = false;
+ if((p_source != null) && (p_target != null))
+ {
+ Ray l_ray = new Ray();
+ l_ray.origin = (p_source.transform.position + p_source.transform.rotation * p_source.center);
+ l_ray.direction = (p_target.transform.position + p_target.transform.rotation * p_target.center) - l_ray.origin;
+ List l_hits = Physics.RaycastAll(l_ray, p_limit, LayerMask.NameToLayer("UI Internal")).ToList();
+ if(l_hits.Count > 0)
+ {
+ l_hits.Sort((a, b) => ((a.distance < b.distance) ? -1 : 1));
+ l_result = (l_hits.First().collider.gameObject.transform.root == p_target.transform.root);
+ }
+ }
+ return l_result;
+ }
+
+ // Patches
+ static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear();
+ void OnAvatarClear()
+ {
+ try
+ {
+ if(m_localCopycat != null)
+ m_localCopycat.OnAvatarClear();
+ }
+ catch(Exception e)
+ {
+ MelonLoader.MelonLogger.Error(e);
+ }
+ }
+
+ static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar();
+ void OnSetupAvatar()
+ {
+ try
+ {
+ if(m_localCopycat != null)
+ m_localCopycat.OnAvatarSetup();
+ }
+ catch(Exception e)
+ {
+ MelonLoader.MelonLogger.Error(e);
+ }
+ }
+ }
+}
diff --git a/ml_pmc/ModUi.cs b/ml_pmc/ModUi.cs
new file mode 100644
index 0000000..c537cd2
--- /dev/null
+++ b/ml_pmc/ModUi.cs
@@ -0,0 +1,136 @@
+using BTKUILib.UIObjects;
+using BTKUILib.UIObjects.Components;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+
+namespace ml_pmc
+{
+ static class ModUi
+ {
+ enum UiIndex
+ {
+ Toggle,
+ Position,
+ Rotation,
+ Gestures,
+ LookAtMix,
+ MirrorPose,
+ MirrorPosition,
+ MirrorRotation,
+ Reset
+ }
+
+ internal static Action CopySwitch;
+
+ static List ms_uiElements = null;
+ static string ms_selectedPlayer;
+
+ internal static void Init()
+ {
+ ms_uiElements = new List();
+
+ BTKUILib.QuickMenuAPI.PrepareIcon("PlayerMovementCopycat", "PMC-Dancing", GetIconStream("dancing.png"));
+ BTKUILib.QuickMenuAPI.PrepareIcon("PlayerMovementCopycat", "PMC-Dancing-On", GetIconStream("dancing_on.png"));
+
+ var l_category = BTKUILib.QuickMenuAPI.PlayerSelectPage.AddCategory("Player Movement Copycat", "PlayerMovementCopycat");
+
+ ms_uiElements.Add(l_category.AddButton("Copy movement", "PMC-Dancing", "Start/stop copy of player's movement"));
+ (ms_uiElements[(int)UiIndex.Toggle] as Button).OnPress += OnCopySwitch;
+
+ ms_uiElements.Add(l_category.AddToggle("Apply position", "Apply local position change of target player", Settings.Position));
+ (ms_uiElements[(int)UiIndex.Position] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.Position, value);
+
+ ms_uiElements.Add(l_category.AddToggle("Apply rotation", "Apply local rotation change of target player", Settings.Rotation));
+ (ms_uiElements[(int)UiIndex.Rotation] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.Rotation, value);
+
+ ms_uiElements.Add(l_category.AddToggle("Copy gestures", "Copy gestures of target player", Settings.Gestures));
+ (ms_uiElements[(int)UiIndex.Gestures] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.Gestures, value);
+
+ ms_uiElements.Add(l_category.AddToggle("Apply LookAtIK", "Mix target player pose and camera view direction (desktop only)", Settings.LookAtMix));
+ (ms_uiElements[(int)UiIndex.LookAtMix] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.LookAtMix, value);
+
+ ms_uiElements.Add(l_category.AddToggle("Mirror pose", "Mirror target player pose", Settings.MirrorPose));
+ (ms_uiElements[(int)UiIndex.MirrorPose] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.MirrorPose, value);
+
+ ms_uiElements.Add(l_category.AddToggle("Mirror position", "Mirror target player movement against 0YZ plane", Settings.MirrorPosition));
+ (ms_uiElements[(int)UiIndex.MirrorPosition] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.MirrorPosition, value);
+
+ ms_uiElements.Add(l_category.AddToggle("Mirror rotation", "Mirror target player rotation against 0YZ plane", Settings.MirrorRotation));
+ (ms_uiElements[(int)UiIndex.MirrorRotation] as ToggleButton).OnValueUpdated += (value) => OnToggleUpdate(UiIndex.MirrorRotation, value);
+
+ ms_uiElements.Add(l_category.AddButton("Reset settings", "", "Reset mod's settings to default"));
+ (ms_uiElements[(int)UiIndex.Reset] as Button).OnPress += Reset;
+
+ BTKUILib.QuickMenuAPI.OnPlayerSelected += (_, id) => ms_selectedPlayer = id;
+ PoseCopycat.OnActivityChange += UpdateToggleColor;
+ }
+
+ static void OnCopySwitch() => CopySwitch?.Invoke(ms_selectedPlayer);
+
+ static void OnToggleUpdate(UiIndex p_index, bool p_value, bool p_force = false)
+ {
+ switch(p_index)
+ {
+ case UiIndex.Position:
+ Settings.SetSetting(Settings.ModSetting.Position, p_value);
+ break;
+
+ case UiIndex.Rotation:
+ Settings.SetSetting(Settings.ModSetting.Rotation, p_value);
+ break;
+
+ case UiIndex.Gestures:
+ Settings.SetSetting(Settings.ModSetting.Gestures, p_value);
+ break;
+
+ case UiIndex.LookAtMix:
+ Settings.SetSetting(Settings.ModSetting.LookAtMix, p_value);
+ break;
+
+ case UiIndex.MirrorPose:
+ Settings.SetSetting(Settings.ModSetting.MirrorPose, p_value);
+ break;
+
+ case UiIndex.MirrorPosition:
+ Settings.SetSetting(Settings.ModSetting.MirrorPosition, p_value);
+ break;
+
+ case UiIndex.MirrorRotation:
+ Settings.SetSetting(Settings.ModSetting.MirrorRotation, p_value);
+ break;
+ }
+
+ if(p_force)
+ (ms_uiElements[(int)p_index] as ToggleButton).ToggleValue = p_value;
+ }
+
+ static void Reset()
+ {
+ OnToggleUpdate(UiIndex.Position, true, true);
+ OnToggleUpdate(UiIndex.Rotation, true, true);
+ OnToggleUpdate(UiIndex.Gestures, true, true);
+ OnToggleUpdate(UiIndex.LookAtMix, true, true);
+ OnToggleUpdate(UiIndex.MirrorPose, false, true);
+ OnToggleUpdate(UiIndex.MirrorPosition, false, true);
+ OnToggleUpdate(UiIndex.MirrorRotation, false, true);
+ }
+
+ internal static void ShowAlert(string p_text) => BTKUILib.QuickMenuAPI.ShowAlertToast(p_text, 2);
+
+ // Currently broken in BTKUILib, waiting for fix
+ static void UpdateToggleColor(bool p_state)
+ {
+ //(ms_uiElements[(int)UiIndex.Toggle] as Button).ButtonIcon = (p_state ? "PMC-Dancing-On" : "PMC-Dancing");
+ //(ms_uiElements[(int)UiIndex.Toggle] as Button).ButtonText = (p_state ? "PMC-Dancing-On" : "PMC-Dancing");
+ }
+
+ static Stream GetIconStream(string p_name)
+ {
+ Assembly l_assembly = Assembly.GetExecutingAssembly();
+ string l_assemblyName = l_assembly.GetName().Name;
+ return l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name);
+ }
+ }
+}
diff --git a/ml_pmc/PoseCopycat.cs b/ml_pmc/PoseCopycat.cs
new file mode 100644
index 0000000..5e0bad0
--- /dev/null
+++ b/ml_pmc/PoseCopycat.cs
@@ -0,0 +1,301 @@
+using ABI_RC.Core.Player;
+using ABI_RC.Core.Savior;
+using ABI_RC.Systems.IK;
+using ABI_RC.Systems.IK.SubSystems;
+using ABI_RC.Systems.InputManagement;
+using ABI_RC.Systems.MovementSystem;
+using RootMotion.FinalIK;
+using UnityEngine;
+
+namespace ml_pmc
+{
+ [DisallowMultipleComponent]
+ public class PoseCopycat : MonoBehaviour
+ {
+ static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f);
+
+ static public PoseCopycat Instance { get; private set; } = null;
+ static internal System.Action OnActivityChange;
+
+ Animator m_animator = null;
+ VRIK m_vrIk = null;
+ float m_ikWeight = 1f;
+ LookAtIK m_lookAtIk = null;
+ float m_lookIkWeight = 1f;
+ bool m_sitting = false;
+ bool m_inVr = false;
+
+ bool m_active = false;
+ float m_distanceLimit = float.MaxValue;
+ bool m_fingerTracking = false;
+
+ HumanPoseHandler m_poseHandler = null;
+ HumanPose m_pose;
+ PuppetParser m_puppetParser = null;
+
+ internal PoseCopycat()
+ {
+ if(Instance == null)
+ Instance = this;
+ }
+ ~PoseCopycat()
+ {
+ if(Instance == this)
+ Instance = null;
+ }
+
+ // Unity events
+ void Update()
+ {
+ m_sitting = (MovementSystem.Instance.lastSeat != null);
+
+ if(m_active && (m_puppetParser != null))
+ {
+ OverrideIK();
+
+ if(m_puppetParser.HasAnimator())
+ {
+ bool l_mirror = Settings.MirrorPose;
+
+ if(Settings.Gestures)
+ {
+ CVRInputManager.Instance.gestureLeft = (l_mirror ? m_puppetParser.GetRightGesture() : m_puppetParser.GetLeftGesture());
+ CVRInputManager.Instance.gestureRight = (l_mirror ? m_puppetParser.GetLeftGesture() : m_puppetParser.GetRightGesture());
+ }
+
+ if(m_puppetParser.HasFingerTracking())
+ {
+ m_fingerTracking = true;
+
+ CVRInputManager.Instance.individualFingerTracking = true;
+ IKSystem.Instance.FingerSystem.controlActive = true;
+
+ ref float[] l_curls = ref m_puppetParser.GetFingerCurls();
+
+ CVRInputManager.Instance.fingerCurlLeftThumb = l_curls[l_mirror ? 5 : 0];
+ CVRInputManager.Instance.fingerCurlLeftIndex = l_curls[l_mirror ? 6 : 1];
+ CVRInputManager.Instance.fingerCurlLeftMiddle = l_curls[l_mirror ? 7 : 2];
+ CVRInputManager.Instance.fingerCurlLeftRing = l_curls[l_mirror ? 8 : 3];
+ CVRInputManager.Instance.fingerCurlLeftPinky = l_curls[l_mirror ? 9 : 4];
+ CVRInputManager.Instance.fingerCurlRightThumb = l_curls[l_mirror ? 0 : 5];
+ CVRInputManager.Instance.fingerCurlRightIndex = l_curls[l_mirror ? 1 : 6];
+ CVRInputManager.Instance.fingerCurlRightMiddle = l_curls[l_mirror ? 2 : 7];
+ CVRInputManager.Instance.fingerCurlRightRing = l_curls[l_mirror ? 3 : 8];
+ CVRInputManager.Instance.fingerCurlRightPinky = l_curls[l_mirror ? 4 : 9];
+ }
+ else
+ {
+ if(m_fingerTracking)
+ {
+ RestoreFingerTracking();
+ m_fingerTracking = false;
+ }
+ }
+
+ Matrix4x4 l_offset = m_puppetParser.GetOffset();
+ Vector3 l_pos = l_offset * ms_pointVector;
+ Quaternion l_rot = l_offset.rotation;
+
+ l_pos.y = 0f;
+ if(Settings.MirrorPosition)
+ l_pos.x *= -1f;
+ l_pos = Vector3.ClampMagnitude(l_pos, m_distanceLimit);
+
+ l_rot = Quaternion.Euler(0f, l_rot.eulerAngles.y * (Settings.MirrorRotation ? -1f : 1f), 0f);
+
+ Matrix4x4 l_result = PlayerSetup.Instance.transform.GetMatrix() * Matrix4x4.TRS(l_pos, l_rot, Vector3.one);
+
+ if(Settings.Position && !m_sitting && !m_puppetParser.IsSitting() && Utils.IsWorldSafe() && Utils.IsCombatSafe())
+ PlayerSetup.Instance.transform.position = l_result * ms_pointVector;
+
+ if(Settings.Rotation && !m_sitting && !m_puppetParser.IsSitting() && Utils.IsCombatSafe())
+ {
+ if(m_inVr)
+ {
+ Vector3 l_avatarPos = PlayerSetup.Instance._avatar.transform.position;
+ PlayerSetup.Instance.transform.rotation = l_result.rotation;
+ Vector3 l_dif = l_avatarPos - PlayerSetup.Instance._avatar.transform.position;
+ PlayerSetup.Instance.transform.position += l_dif;
+ }
+ else
+ PlayerSetup.Instance.transform.rotation = l_result.rotation;
+ }
+ }
+ else
+ {
+ if(!m_puppetParser.IsWaitingAnimator())
+ SetTarget(null);
+ }
+
+ if(Vector3.Distance(this.transform.position, m_puppetParser.transform.position) > m_distanceLimit)
+ SetTarget(null);
+ }
+ }
+
+ void LateUpdate()
+ {
+ if(m_active && (m_animator != null) && (m_puppetParser != null) && m_puppetParser.IsPoseParsed())
+ {
+ OverrideIK();
+
+ m_puppetParser.GetPose().CopyTo(ref m_pose);
+ if(Settings.MirrorPose)
+ Utils.MirrorPose(ref m_pose);
+ m_poseHandler.SetHumanPose(ref m_pose);
+ }
+ }
+
+ // Patches
+ internal void OnAvatarClear()
+ {
+ m_inVr = Utils.IsInVR();
+
+ if(m_puppetParser != null)
+ Object.Destroy(m_puppetParser);
+ m_puppetParser = null;
+
+ m_animator = null;
+ m_vrIk = null;
+ m_lookAtIk = null;
+
+ m_poseHandler?.Dispose();
+ m_poseHandler = null;
+ m_active = false;
+ m_distanceLimit = float.MaxValue;
+ m_fingerTracking = false;
+ m_pose = new HumanPose();
+ }
+ internal void OnAvatarSetup()
+ {
+ m_animator = PlayerSetup.Instance._animator;
+ m_vrIk = PlayerSetup.Instance._avatar.GetComponent();
+ m_lookAtIk = PlayerSetup.Instance._avatar.GetComponent();
+
+ if((m_animator != null) && m_animator.isHuman)
+ {
+ m_poseHandler = new HumanPoseHandler(m_animator.avatar, m_animator.transform);
+ m_poseHandler.GetHumanPose(ref m_pose);
+
+ if(m_vrIk != null)
+ {
+ m_vrIk.onPreSolverUpdate.AddListener(this.OnVRIKPreUpdate);
+ m_vrIk.onPostSolverUpdate.AddListener(this.OnVRIKPostUpdate);
+ }
+
+ if(m_lookAtIk != null)
+ {
+ m_lookAtIk.onPreSolverUpdate.AddListener(this.OnLookAtIKPreUpdate);
+ m_lookAtIk.onPostSolverUpdate.AddListener(this.OnLookAtIKPostUpdate);
+ }
+ }
+ else
+ m_animator = null;
+ }
+
+ // IK updates
+ void OnVRIKPreUpdate()
+ {
+ if(m_active)
+ {
+ m_ikWeight = m_vrIk.solver.IKPositionWeight;
+ m_vrIk.solver.IKPositionWeight = 0f;
+ }
+ }
+ void OnVRIKPostUpdate()
+ {
+ if(m_active)
+ m_vrIk.solver.IKPositionWeight = m_ikWeight;
+ }
+
+ void OnLookAtIKPreUpdate()
+ {
+ if(m_active && !Settings.LookAtMix)
+ {
+ m_lookIkWeight = m_lookAtIk.solver.IKPositionWeight;
+ m_lookAtIk.solver.IKPositionWeight = 0f;
+ }
+ }
+ void OnLookAtIKPostUpdate()
+ {
+ if(m_active && !Settings.LookAtMix)
+ m_lookAtIk.solver.IKPositionWeight = m_lookIkWeight;
+ }
+
+ // Arbitrary
+ public void SetTarget(GameObject p_target)
+ {
+ if(m_animator != null)
+ {
+ if(!m_active)
+ {
+ if(p_target != null)
+ {
+ m_puppetParser = p_target.AddComponent();
+ m_distanceLimit = Utils.GetWorldMovementLimit();
+
+ m_active = true;
+ OnActivityChange?.Invoke(m_active);
+ }
+ }
+ else
+ {
+ if(p_target == null)
+ {
+ if(m_puppetParser != null)
+ Object.Destroy(m_puppetParser);
+ m_puppetParser = null;
+
+ if(!m_sitting)
+ {
+ Quaternion l_rot = PlayerSetup.Instance.transform.rotation;
+ PlayerSetup.Instance.transform.rotation = Quaternion.Euler(0f, l_rot.eulerAngles.y, 0f);
+ }
+
+ RestoreIK();
+ RestoreFingerTracking();
+ m_fingerTracking = false;
+
+ m_active = false;
+ OnActivityChange?.Invoke(m_active);
+ }
+ }
+ }
+ }
+
+ public bool IsActive() => m_active;
+ public bool IsFingerTrackingActive() => m_fingerTracking;
+
+ void OverrideIK()
+ {
+ if((m_vrIk != null) && !BodySystem.isCalibrating)
+ BodySystem.TrackingPositionWeight = 0f;
+ }
+ void RestoreIK()
+ {
+ if((m_vrIk != null) && !BodySystem.isCalibrating)
+ {
+ BodySystem.TrackingPositionWeight = 1f;
+ m_vrIk.solver.Reset();
+ }
+ }
+ void RestoreFingerTracking()
+ {
+ CVRInputManager.Instance.individualFingerTracking = (m_inVr && Utils.AreKnucklesInUse() && !CVRInputManager._moduleXR.GestureToggleValue);
+ IKSystem.Instance.FingerSystem.controlActive = CVRInputManager.Instance.individualFingerTracking;
+
+ if(!CVRInputManager.Instance.individualFingerTracking)
+ {
+ CVRInputManager.Instance.fingerCurlLeftThumb = 0f;
+ CVRInputManager.Instance.fingerCurlLeftIndex = 0f;
+ CVRInputManager.Instance.fingerCurlLeftMiddle = 0f;
+ CVRInputManager.Instance.fingerCurlLeftRing = 0f;
+ CVRInputManager.Instance.fingerCurlLeftPinky = 0f;
+ CVRInputManager.Instance.fingerCurlRightThumb = 0f;
+ CVRInputManager.Instance.fingerCurlRightIndex = 0f;
+ CVRInputManager.Instance.fingerCurlRightMiddle = 0f;
+ CVRInputManager.Instance.fingerCurlRightRing = 0f;
+ CVRInputManager.Instance.fingerCurlRightPinky = 0f;
+ }
+ }
+ }
+}
diff --git a/ml_fpt/Properties/AssemblyInfo.cs b/ml_pmc/Properties/AssemblyInfo.cs
similarity index 52%
rename from ml_fpt/Properties/AssemblyInfo.cs
rename to ml_pmc/Properties/AssemblyInfo.cs
index 388da0e..0f573da 100644
--- a/ml_fpt/Properties/AssemblyInfo.cs
+++ b/ml_pmc/Properties/AssemblyInfo.cs
@@ -1,10 +1,8 @@
using System.Reflection;
-[assembly: AssemblyTitle("FourPointTracking")]
-[assembly: AssemblyVersion("1.0.9")]
-[assembly: AssemblyFileVersion("1.0.9")]
-
-[assembly: MelonLoader.MelonInfo(typeof(ml_fpt.FourPointTracking), "FourPointTracking", "1.0.9", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
+[assembly: MelonLoader.MelonInfo(typeof(ml_pmc.PlayerMovementCopycat), "PlayerMovementCopycat", "1.0.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
+[assembly: MelonLoader.MelonPriority(3)]
+[assembly: MelonLoader.MelonAdditionalDependencies("BTKUILib")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
\ No newline at end of file
diff --git a/ml_pmc/PuppetParser.cs b/ml_pmc/PuppetParser.cs
new file mode 100644
index 0000000..531795a
--- /dev/null
+++ b/ml_pmc/PuppetParser.cs
@@ -0,0 +1,156 @@
+using ABI_RC.Core.Player;
+using UnityEngine;
+
+namespace ml_pmc
+{
+ [DisallowMultipleComponent]
+ class PuppetParser : MonoBehaviour
+ {
+ static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f);
+
+ PuppetMaster m_puppetMaster = null;
+ Animator m_animator = null;
+ AnimatorCullingMode m_cullMode;
+ float m_armatureScale = 1f;
+ float m_armatureHeight = 0f;
+
+ bool m_waitAnimator = true;
+ HumanPoseHandler m_poseHandler = null;
+ HumanPose m_pose;
+ bool m_poseParsed = false;
+
+ Matrix4x4 m_matrix = Matrix4x4.identity;
+ Matrix4x4 m_offset = Matrix4x4.identity;
+
+ bool m_sitting = false;
+ float m_leftGesture = 0f;
+ float m_rightGesture = 0f;
+ bool m_fingerTracking = false;
+ float[] m_fingerCurls = null;
+
+ internal PuppetParser()
+ {
+ m_fingerCurls = new float[10];
+ }
+
+ // Unity events
+ void Start()
+ {
+ m_puppetMaster = this.GetComponent();
+ m_matrix = this.transform.GetMatrix();
+ StartCoroutine(WaitForAnimator());
+ }
+
+ void OnDestroy()
+ {
+ if(m_animator != null)
+ m_animator.cullingMode = m_cullMode;
+
+ m_poseHandler?.Dispose();
+ }
+
+ void Update()
+ {
+ if(m_puppetMaster != null)
+ {
+ m_sitting = m_puppetMaster.PlayerAvatarMovementDataInput.AnimatorSitting;
+ m_leftGesture = m_puppetMaster.PlayerAvatarMovementDataInput.AnimatorGestureLeft;
+ m_rightGesture = m_puppetMaster.PlayerAvatarMovementDataInput.AnimatorGestureRight;
+ m_fingerTracking = m_puppetMaster.PlayerAvatarMovementDataInput.IndexUseIndividualFingers;
+ if(m_fingerTracking)
+ {
+ m_fingerCurls[0] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftThumbCurl;
+ m_fingerCurls[1] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftIndexCurl;
+ m_fingerCurls[2] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftMiddleCurl;
+ m_fingerCurls[3] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftRingCurl;
+ m_fingerCurls[4] = m_puppetMaster.PlayerAvatarMovementDataInput.LeftPinkyCurl;
+ m_fingerCurls[5] = m_puppetMaster.PlayerAvatarMovementDataInput.RightThumbCurl;
+ m_fingerCurls[6] = m_puppetMaster.PlayerAvatarMovementDataInput.RightIndexCurl;
+ m_fingerCurls[7] = m_puppetMaster.PlayerAvatarMovementDataInput.RightMiddleCurl;
+ m_fingerCurls[8] = m_puppetMaster.PlayerAvatarMovementDataInput.RightRingCurl;
+ m_fingerCurls[9] = m_puppetMaster.PlayerAvatarMovementDataInput.RightPinkyCurl;
+ }
+ }
+
+ if(!ReferenceEquals(m_animator, null))
+ {
+ if(m_animator != null)
+ {
+ Matrix4x4 l_current = this.transform.GetMatrix();
+ m_offset = m_matrix.inverse * l_current;
+ m_matrix = l_current;
+ }
+ else
+ Reset();
+ }
+ }
+
+ void LateUpdate()
+ {
+ if(m_animator != null)
+ {
+ m_poseHandler.GetHumanPose(ref m_pose);
+ m_pose.bodyPosition *= m_armatureScale;
+ m_pose.bodyPosition.y += m_armatureHeight;
+ m_poseParsed = true;
+ }
+ }
+
+ // Arbitrary
+ System.Collections.IEnumerator WaitForAnimator()
+ {
+ while(m_puppetMaster.avatarObject == null)
+ yield return null;
+
+ while(m_animator == null)
+ {
+ m_animator = m_puppetMaster.avatarObject.GetComponent();
+ yield return null;
+ }
+
+ if(m_animator.isHuman)
+ {
+ m_cullMode = m_animator.cullingMode;
+ m_animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
+
+ Transform l_hips = m_animator.GetBoneTransform(HumanBodyBones.Hips);
+ if((l_hips != null) && (l_hips.parent != null))
+ {
+ m_armatureScale = l_hips.parent.localScale.y;
+ m_armatureHeight = ((m_puppetMaster.transform.GetMatrix().inverse * l_hips.parent.GetMatrix()) * ms_pointVector).y;
+ }
+
+ m_poseHandler = new HumanPoseHandler(m_animator.avatar, m_animator.transform);
+ m_matrix = this.transform.GetMatrix();
+ }
+ else
+ Reset();
+
+ m_waitAnimator = false;
+ }
+
+ void Reset()
+ {
+ m_animator = null;
+ m_poseHandler?.Dispose();
+ m_poseHandler = null;
+ m_pose = new HumanPose();
+ m_poseParsed = false;
+ m_offset = Matrix4x4.identity;
+ m_sitting = false;
+ m_leftGesture = 0f;
+ m_rightGesture = 0f;
+ }
+
+ public bool IsWaitingAnimator() => m_waitAnimator;
+ public bool HasAnimator() => !ReferenceEquals(m_animator, null);
+ public ref HumanPose GetPose() => ref m_pose;
+ public bool IsPoseParsed() => m_poseParsed;
+ public ref Matrix4x4 GetOffset() => ref m_offset;
+ public bool IsSitting() => m_sitting;
+ public float GetLeftGesture() => m_leftGesture;
+ public float GetRightGesture() => m_rightGesture;
+ public bool HasFingerTracking() => m_fingerTracking;
+ public ref float[] GetFingerCurls() => ref m_fingerCurls;
+ }
+}
diff --git a/ml_pmc/Settings.cs b/ml_pmc/Settings.cs
new file mode 100644
index 0000000..d412dc2
--- /dev/null
+++ b/ml_pmc/Settings.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+
+namespace ml_pmc
+{
+ static class Settings
+ {
+ public enum ModSetting
+ {
+ Position,
+ Rotation,
+ Gestures,
+ LookAtMix,
+ MirrorPose,
+ MirrorPosition,
+ MirrorRotation
+ }
+
+ public static bool Position { get; private set; } = true;
+ public static bool Rotation { get; private set; } = true;
+ public static bool Gestures { get; private set; } = true;
+ public static bool LookAtMix { get; private set; } = true;
+ public static bool MirrorPose { get; private set; } = false;
+ public static bool MirrorPosition { get; private set; } = false;
+ public static bool MirrorRotation { get; private set; } = false;
+
+ public static Action PositionChange;
+ public static Action RotationChange;
+ public static Action GesturesChange;
+ public static Action LookAtMixChange;
+ public static Action MirrorPoseChange;
+ public static Action MirrorPositionChange;
+ public static Action MirrorRotationChange;
+
+ static MelonLoader.MelonPreferences_Category ms_category = null;
+ static List ms_entries = null;
+
+ internal static void Init()
+ {
+ ms_category = MelonLoader.MelonPreferences.CreateCategory("PMC", null, true);
+ ms_entries = new List()
+ {
+ ms_category.CreateEntry(ModSetting.Position.ToString(), Position),
+ ms_category.CreateEntry(ModSetting.Rotation.ToString(), Rotation),
+ ms_category.CreateEntry(ModSetting.Gestures.ToString(), Gestures),
+ ms_category.CreateEntry(ModSetting.LookAtMix.ToString(), LookAtMix),
+ ms_category.CreateEntry(ModSetting.MirrorPose.ToString(), MirrorPose),
+ ms_category.CreateEntry(ModSetting.MirrorPosition.ToString(), MirrorPosition),
+ ms_category.CreateEntry(ModSetting.MirrorRotation.ToString(), MirrorRotation),
+ };
+
+ Position = (bool)ms_entries[(int)ModSetting.Position].BoxedValue;
+ Rotation = (bool)ms_entries[(int)ModSetting.Rotation].BoxedValue;
+ Gestures = (bool)ms_entries[(int)ModSetting.Gestures].BoxedValue;
+ LookAtMix = (bool)ms_entries[(int)ModSetting.LookAtMix].BoxedValue;
+ MirrorPose = (bool)ms_entries[(int)ModSetting.MirrorPose].BoxedValue;
+ MirrorPosition = (bool)ms_entries[(int)ModSetting.MirrorPosition].BoxedValue;
+ MirrorRotation = (bool)ms_entries[(int)ModSetting.MirrorRotation].BoxedValue;
+ }
+
+ public static void SetSetting(ModSetting p_setting, object p_value)
+ {
+ switch(p_setting)
+ {
+ case ModSetting.Position:
+ {
+ Position = (bool)p_value;
+ PositionChange?.Invoke((bool)p_value);
+ }
+ break;
+
+ case ModSetting.Rotation:
+ {
+ Rotation = (bool)p_value;
+ RotationChange?.Invoke((bool)p_value);
+ break;
+ }
+
+ case ModSetting.Gestures:
+ {
+ Gestures = (bool)p_value;
+ GesturesChange?.Invoke((bool)p_value);
+ }
+ break;
+
+ case ModSetting.LookAtMix:
+ {
+ LookAtMix = (bool)p_value;
+ LookAtMixChange?.Invoke((bool)p_value);
+ }
+ break;
+
+ //
+ case ModSetting.MirrorPose:
+ {
+ MirrorPose = (bool)p_value;
+ MirrorPoseChange?.Invoke((bool)p_value);
+ }
+ break;
+
+ case ModSetting.MirrorPosition:
+ {
+ MirrorPosition = (bool)p_value;
+ MirrorPositionChange?.Invoke((bool)p_value);
+ }
+ break;
+
+ case ModSetting.MirrorRotation:
+ {
+ MirrorRotation = (bool)p_value;
+ MirrorRotationChange?.Invoke((bool)p_value);
+ }
+ break;
+ }
+
+ if(ms_entries != null)
+ ms_entries[(int)p_setting].BoxedValue = p_value;
+ }
+ }
+}
diff --git a/ml_pmc/Utils.cs b/ml_pmc/Utils.cs
new file mode 100644
index 0000000..0b93404
--- /dev/null
+++ b/ml_pmc/Utils.cs
@@ -0,0 +1,79 @@
+using ABI.CCK.Components;
+using ABI_RC.Core.Player;
+using ABI_RC.Core.Savior;
+using ABI_RC.Systems.InputManagement;
+using System.Linq;
+using System.Reflection;
+using UnityEngine;
+
+namespace ml_pmc
+{
+ static class Utils
+ {
+ static readonly (int, int)[] ms_sideMuscles = new (int, int)[]
+ {
+ (29,21), (30,22), (31,23), (32,24), (33,25), (34,26), (35,27), (36,28),
+ (46,37), (47,38), (48,39), (49,40), (50,41), (51,42), (52,43), (53,44), (54,45),
+ (75,55), (76,56), (77,57), (78,58), (79,59), (80,60), (81,61), (82,62), (83,63), (84,64),
+ (85,65), (86,66), (87,67), (88,68), (89, 69), (90,70), (91,71), (92,72), (93,73), (94,74)
+ };
+ static readonly int[] ms_centralMuscles = new int[] { 1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 18, 20 };
+
+ public static bool IsInVR() => ((CheckVR.Instance != null) && CheckVR.Instance.hasVrDeviceLoaded);
+ public static bool AreKnucklesInUse() => ((CVRInputManager.Instance._leftController == ABI_RC.Systems.InputManagement.XR.EXRControllerType.Index) || (CVRInputManager.Instance._rightController == ABI_RC.Systems.InputManagement.XR.EXRControllerType.Index));
+
+ public static bool IsWorldSafe() => ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying);
+ public static bool IsCombatSafe() => ((CombatSystem.Instance == null) || !CombatSystem.Instance.isDown);
+
+ public static float GetWorldMovementLimit()
+ {
+ float l_result = 1f;
+ if(CVRWorld.Instance != null)
+ {
+ l_result = CVRWorld.Instance.baseMovementSpeed;
+ l_result *= CVRWorld.Instance.sprintMultiplier;
+ l_result *= CVRWorld.Instance.inAirMovementMultiplier;
+ l_result *= CVRWorld.Instance.flyMultiplier;
+ }
+ return l_result;
+ }
+
+ public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false)
+ {
+ return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.lossyScale : Vector3.one);
+ }
+
+ public static void CopyTo(this HumanPose p_source, ref HumanPose p_target)
+ {
+ p_target.bodyPosition = p_source.bodyPosition;
+ p_target.bodyRotation = p_source.bodyRotation;
+
+ int l_count = Mathf.Min(p_source.muscles.Length, p_target.muscles.Length);
+ for(int i = 0; i < l_count; i++)
+ p_target.muscles[i] = p_source.muscles[i];
+ }
+
+ public static void MirrorPose(ref HumanPose p_pose)
+ {
+ int l_count = p_pose.muscles.Length;
+ foreach(var l_pair in ms_sideMuscles)
+ {
+ if((l_count > l_pair.Item1) && (l_count > l_pair.Item2))
+ {
+ float l_temp = p_pose.muscles[l_pair.Item1];
+ p_pose.muscles[l_pair.Item1] = p_pose.muscles[l_pair.Item2];
+ p_pose.muscles[l_pair.Item2] = l_temp;
+ }
+ }
+ foreach(int l_index in ms_centralMuscles)
+ {
+ if(l_count > l_index)
+ p_pose.muscles[l_index] *= -1f;
+ }
+
+ p_pose.bodyRotation.x *= -1f;
+ p_pose.bodyRotation.w *= -1f;
+ p_pose.bodyPosition.x *= -1f;
+ }
+ }
+}
diff --git a/ml_pmc/ml_pmc.csproj b/ml_pmc/ml_pmc.csproj
new file mode 100644
index 0000000..946425c
--- /dev/null
+++ b/ml_pmc/ml_pmc.csproj
@@ -0,0 +1,46 @@
+
+
+
+ netstandard2.1
+ x64
+ PlayerMovementCopycat
+ SDraw
+ None
+ PlayerMovementCopycat
+
+
+
+
+
+
+
+
+ D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll
+
+
+ D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll
+
+
+ D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll
+
+
+ D:\Games\Steam\steamapps\common\ChilloutVR\Mods\[broken]\BTKUILib.dll
+
+
+ D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll
+
+
+ D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll
+
+
+ D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll
+
+
+ D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll
+
+
+ D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.PhysicsModule.dll
+
+
+
+
diff --git a/ml_pmc/resources/dancing.png b/ml_pmc/resources/dancing.png
new file mode 100644
index 0000000..d54c17c
Binary files /dev/null and b/ml_pmc/resources/dancing.png differ
diff --git a/ml_pmc/resources/dancing_on.png b/ml_pmc/resources/dancing_on.png
new file mode 100644
index 0000000..ff60d8b
Binary files /dev/null and b/ml_pmc/resources/dancing_on.png differ
diff --git a/ml_prm/AvatarBoolParameter.cs b/ml_prm/AvatarBoolParameter.cs
new file mode 100644
index 0000000..b7a9535
--- /dev/null
+++ b/ml_prm/AvatarBoolParameter.cs
@@ -0,0 +1,43 @@
+using ABI_RC.Core;
+using System.Text.RegularExpressions;
+using UnityEngine;
+
+namespace ml_prm
+{
+ class AvatarBoolParameter
+ {
+ public readonly string m_name;
+ public readonly int m_hash = 0;
+ public readonly bool m_sync;
+ readonly CVRAnimatorManager m_manager = null;
+
+ public AvatarBoolParameter(string p_name, CVRAnimatorManager p_manager)
+ {
+ m_name = p_name;
+ m_manager = p_manager;
+
+ Regex l_regex = new Regex("^#?" + p_name + '$');
+ foreach(var l_param in m_manager.animator.parameters)
+ {
+ if(l_regex.IsMatch(l_param.name) && (l_param.type == AnimatorControllerParameterType.Bool))
+ {
+ m_name = l_param.name;
+ m_hash = l_param.nameHash;
+ m_sync = (l_param.name[0] != '#');
+ break;
+ }
+ }
+ }
+
+ public void SetValue(bool p_value)
+ {
+ if(m_hash != 0)
+ {
+ if(m_sync)
+ m_manager.SetAnimatorParameterBool(m_name, p_value);
+ else
+ m_manager.animator.SetBool(m_hash, p_value);
+ }
+ }
+ }
+}
diff --git a/ml_prm/Main.cs b/ml_prm/Main.cs
new file mode 100644
index 0000000..0cd764d
--- /dev/null
+++ b/ml_prm/Main.cs
@@ -0,0 +1,216 @@
+using ABI.CCK.Components;
+using ABI_RC.Core;
+using ABI_RC.Core.InteractionSystem;
+using ABI_RC.Core.Player;
+using ABI_RC.Core.Util.AssetFiltering;
+using ABI_RC.Systems.IK.SubSystems;
+using ABI_RC.Systems.MovementSystem;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace ml_prm
+{
+ public class PlayerRagdollMod : MelonLoader.MelonMod
+ {
+ static PlayerRagdollMod ms_instance = null;
+
+ RagdollController m_localController = null;
+
+ public override void OnInitializeMelon()
+ {
+ if(ms_instance == null)
+ ms_instance = this;
+
+ Settings.Init();
+ ModUi.Init();
+
+ HarmonyInstance.Patch(
+ typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)),
+ null,
+ new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
+ );
+ HarmonyInstance.Patch(
+ typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)),
+ null,
+ new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
+ );
+ HarmonyInstance.Patch(
+ typeof(PlayerSetup).GetMethod("SetupIKScaling", BindingFlags.NonPublic | BindingFlags.Instance),
+ null,
+ new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnSetupIKScaling_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
+ );
+ HarmonyInstance.Patch(
+ typeof(CVRSeat).GetMethod(nameof(CVRSeat.SitDown)),
+ new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnCVRSeatSitDown_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
+ null
+ );
+ HarmonyInstance.Patch(
+ typeof(BodySystem).GetMethod(nameof(BodySystem.StartCalibration)),
+ new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnStartCalibration_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
+ null
+ );
+ HarmonyInstance.Patch(
+ typeof(RootLogic).GetMethod(nameof(RootLogic.SpawnOnWorldInstance)),
+ new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnWorldSpawn_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
+ null
+ );
+ HarmonyInstance.Patch(
+ typeof(CombatSystem).GetMethods().First(m => (!m.IsGenericMethod && m.Name == nameof(CombatSystem.Down))),
+ new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnCombatDown_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
+ null
+ );
+ HarmonyInstance.Patch(
+ typeof(MovementSystem).GetMethod(nameof(MovementSystem.ToggleFlight)),
+ null,
+ new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnToggleFlight_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
+ );
+
+ // Whitelist the toggle script
+ (typeof(SharedFilter).GetField("_localComponentWhitelist", BindingFlags.NonPublic | BindingFlags.Static)?.GetValue(null) as HashSet)?.Add(typeof(RagdollToggle));
+
+ MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
+ }
+
+ public override void OnDeinitializeMelon()
+ {
+ if(ms_instance == this)
+ ms_instance = null;
+
+ m_localController = null;
+ }
+
+ System.Collections.IEnumerator WaitForLocalPlayer()
+ {
+ while(PlayerSetup.Instance == null)
+ yield return null;
+
+ m_localController = PlayerSetup.Instance.gameObject.AddComponent();
+ ModUi.SwitchChange += this.OnSwitchActivation;
+ }
+
+ void OnSwitchActivation()
+ {
+ if(m_localController != null)
+ m_localController.SwitchRagdoll();
+ }
+
+ // Patches
+ static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear();
+ void OnAvatarClear()
+ {
+ try
+ {
+ if(m_localController != null)
+ m_localController.OnAvatarClear();
+ }
+ catch(Exception e)
+ {
+ MelonLoader.MelonLogger.Error(e);
+ }
+ }
+
+ static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar();
+ void OnSetupAvatar()
+ {
+ try
+ {
+ if(m_localController != null)
+ m_localController.OnAvatarSetup();
+ }
+ catch(Exception e)
+ {
+ MelonLoader.MelonLogger.Error(e);
+ }
+ }
+
+ static void OnSetupIKScaling_Postfix(ref UnityEngine.Vector3 ___scaleDifference) => ms_instance?.OnSetupIKScaling(___scaleDifference.y);
+ void OnSetupIKScaling(float p_scaleDifference)
+ {
+ try
+ {
+ if(m_localController != null)
+ m_localController.OnAvatarScaling(1f + p_scaleDifference);
+ }
+ catch(Exception e)
+ {
+ MelonLoader.MelonLogger.Error(e);
+ }
+ }
+
+ static void OnCVRSeatSitDown_Prefix(ref CVRSeat __instance) => ms_instance?.OnCVRSeatSitDown(__instance);
+ void OnCVRSeatSitDown(CVRSeat p_seat)
+ {
+ try
+ {
+ if(m_localController != null)
+ m_localController.OnSeatSitDown(p_seat);
+ }
+ catch(Exception e)
+ {
+ MelonLoader.MelonLogger.Error(e);
+ }
+ }
+
+ static void OnStartCalibration_Prefix() => ms_instance?.OnStartCalibration();
+ void OnStartCalibration()
+ {
+ try
+ {
+ if(m_localController != null)
+ m_localController.OnStartCalibration();
+ }
+ catch(Exception e)
+ {
+ MelonLoader.MelonLogger.Error(e);
+ }
+ }
+
+ static void OnWorldSpawn_Prefix() => ms_instance?.OnWorldSpawn();
+ void OnWorldSpawn()
+ {
+ try
+ {
+ if(m_localController != null)
+ m_localController.OnWorldSpawn();
+ }
+ catch(Exception e)
+ {
+ MelonLoader.MelonLogger.Error(e);
+ }
+ }
+
+ static void OnCombatDown_Prefix(ref CombatSystem __instance)
+ {
+ if((__instance == CombatSystem.Instance) && !__instance.isDown)
+ ms_instance?.OnCombatDown();
+ }
+ void OnCombatDown()
+ {
+ try
+ {
+ if(m_localController != null)
+ m_localController.OnCombatDown();
+ }
+ catch(Exception e)
+ {
+ MelonLoader.MelonLogger.Error(e);
+ }
+ }
+
+ static void OnToggleFlight_Postfix() => ms_instance?.OnToggleFlight();
+ void OnToggleFlight()
+ {
+ try
+ {
+ if(m_localController != null)
+ m_localController.OnToggleFlight();
+ }
+ catch(Exception e)
+ {
+ MelonLoader.MelonLogger.Error(e);
+ }
+ }
+ }
+}
diff --git a/ml_prm/ModUi.cs b/ml_prm/ModUi.cs
new file mode 100644
index 0000000..2a77f31
--- /dev/null
+++ b/ml_prm/ModUi.cs
@@ -0,0 +1,198 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+
+namespace ml_prm
+{
+ static class ModUi
+ {
+ enum UiIndex
+ {
+ Hotkey = 0,
+ Gravity,
+ PointersReaction,
+ IgnoreLocal,
+ CombatReaction,
+ AutoRecover,
+ Slipperiness,
+ Bounciness,
+ ViewVelocity,
+ JumpRecover,
+ VelocityMultiplier,
+ MovementDrag,
+ AngularDrag,
+ RecoverDelay
+ }
+
+ static public event Action SwitchChange;
+
+ static List