Moved PlayerRagdollMod and DesktopHeadTracking to archived

Fixed for r173 game build
This commit is contained in:
SDraw 2023-11-10 02:12:12 +03:00
parent a232c2ce13
commit 9886bdc154
No known key found for this signature in database
GPG key ID: BB95B4DAB2BB8BB5
43 changed files with 3490 additions and 3600 deletions

View file

@ -3,20 +3,20 @@ Merged set of MelonLoader mods for ChilloutVR.
**Table for game build 2023r172p1:** **Table for game build 2023r172p1:**
| Full name | Short name | Latest version | Available in [CVRMA](https://github.com/knah/CVRMelonAssistant) | | Full name | Short name | Latest version | Available in [CVRMA](https://github.com/knah/CVRMelonAssistant) |
|:---------:|:----------:|:--------------:| :----------------------------------------------------------------| |:---------:|:----------:|:--------------:| :----------------------------------------------------------------|
| [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | 1.3.3 [:arrow_down:](../../releases/latest/download/ml_amt.dll)| ✔ Yes | | [Avatar Motion Tweaker](/ml_amt/README.md) | ml_amt | 1.3.4 [:arrow_down:](../../releases/latest/download/ml_amt.dll)| ✔ Yes<br>:hourglass_flowing_sand: Update review |
| [Desktop Head Tracking](/ml_dht/README.md)| ml_dht | - | ✔ Yes<br>:warning:Broken | | [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.4.4 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| ✔ Yes<br>:hourglass_flowing_sand: Update review |
| [Leap Motion Extension](/ml_lme/README.md)| ml_lme | 1.4.3 [:arrow_down:](../../releases/latest/download/ml_lme.dll)| ✔ Yes |
| [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.0.8 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| ✔ Yes | | [Pickup Arm Movement](/ml_pam/README.md)| ml_pam | 1.0.8 [:arrow_down:](../../releases/latest/download/ml_pam.dll)| ✔ Yes |
| [Player Movement Copycat](/ml_pmc/README.md)| ml_pmc | 1.0.3 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)| ✔ Yes | | [Player Movement Copycat](/ml_pmc/README.md)| ml_pmc | 1.0.3 [:arrow_down:](../../releases/latest/download/ml_pmc.dll)| ✔ Yes |
| [Player Ragdoll Mod](/ml_prm/README.md)| ml_prm | 1.0.11 [:arrow_down:](../../releases/latest/download/ml_prm.dll)| ✔ Yes<br>:hourglass_flowing_sand: Update review |
| [Vive Extended Input](/ml_vei/README.md) | ml_vei | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_vei.dll)| ✔ Yes | | [Vive Extended Input](/ml_vei/README.md) | ml_vei | 1.0.0 [:arrow_down:](../../releases/latest/download/ml_vei.dll)| ✔ Yes |
**Archived mods:** **Archived mods:**
| Full name | Short name | Notes | | Full name | Short name | Notes |
|:---------:|:----------:|-------| |:---------:|:----------:|-------|
| Avatar Change Info | ml_aci | Superseded by `Extended Game Notifications` | Avatar Change Info | ml_aci | Superseded by `Extended Game Notifications` |
| Desktop Reticle Switch | ml_drs | Boring functionality | Desktop Head Tracking | ml_dht | Unable to emulate fake data |
| Extended Game Notifications | ml_egn | In-game feature sine 2023r172 update | Desktop Reticle Switch | ml_drs | Boring functionality |
| Four Point Tracking | ml_fpt | In-game feature since 2022r170 update | Extended Game Notifications | ml_egn | In-game feature sine 2023r172 update |
| Game Main Fixes | ml_gmf | In-game feature sine 2023r172 update | Four Point Tracking | ml_fpt | In-game feature since 2022r170 update |
| Game Main Fixes | ml_gmf | In-game feature sine 2023r172 update |
| Player Ragdoll Mod | ml_prm | Unable to fix offset problems |
| Server Connection Info | ml_sci | Superseded by `Extended Game Notifications` | Server Connection Info | ml_sci | Superseded by `Extended Game Notifications`

View file

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 291 KiB

Before After
Before After

View file

@ -1,185 +1,185 @@
using ABI.CCK.Components; using ABI.CCK.Components;
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using RootMotion.FinalIK; using RootMotion.FinalIK;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using ViveSR.anipal.Lip; using ViveSR.anipal.Lip;
namespace ml_dht namespace ml_dht
{ {
[DisallowMultipleComponent] [DisallowMultipleComponent]
class HeadTracked : MonoBehaviour class HeadTracked : MonoBehaviour
{ {
static FieldInfo ms_emotePlaying = typeof(PlayerSetup).GetField("_emotePlaying", BindingFlags.NonPublic | BindingFlags.Instance); static FieldInfo ms_emotePlaying = typeof(PlayerSetup).GetField("_emotePlaying", BindingFlags.NonPublic | BindingFlags.Instance);
bool m_enabled = false; bool m_enabled = false;
bool m_headTracking = true; bool m_headTracking = true;
bool m_blinking = true; bool m_blinking = true;
bool m_eyeTracking = true; bool m_eyeTracking = true;
float m_smoothing = 0.5f; float m_smoothing = 0.5f;
bool m_mirrored = false; bool m_mirrored = false;
bool m_faceOverride = true; bool m_faceOverride = true;
CVRAvatar m_avatarDescriptor = null; CVRAvatar m_avatarDescriptor = null;
LookAtIK m_lookIK = null; LookAtIK m_lookIK = null;
Transform m_headBone = null; Transform m_headBone = null;
Vector3 m_headPosition; Vector3 m_headPosition;
Quaternion m_headRotation; Quaternion m_headRotation;
Vector2 m_gazeDirection; Vector2 m_gazeDirection;
float m_blinkProgress = 0f; float m_blinkProgress = 0f;
Vector2 m_mouthShapes; Vector2 m_mouthShapes;
float m_eyebrowsProgress = 0f; float m_eyebrowsProgress = 0f;
Quaternion m_bindRotation; Quaternion m_bindRotation;
Quaternion m_lastHeadRotation; Quaternion m_lastHeadRotation;
// Unity events // Unity events
void Start() void Start()
{ {
Settings.EnabledChange += this.SetEnabled; Settings.EnabledChange += this.SetEnabled;
Settings.HeadTrackingChange += this.SetHeadTracking; Settings.HeadTrackingChange += this.SetHeadTracking;
Settings.EyeTrackingChange += this.SetEyeTracking; Settings.EyeTrackingChange += this.SetEyeTracking;
Settings.BlinkingChange += this.SetBlinking; Settings.BlinkingChange += this.SetBlinking;
Settings.SmoothingChange += this.SetSmoothing; Settings.SmoothingChange += this.SetSmoothing;
Settings.MirroredChange += this.SetMirrored; Settings.MirroredChange += this.SetMirrored;
Settings.FaceOverrideChange += this.SetFaceOverride; Settings.FaceOverrideChange += this.SetFaceOverride;
} }
void OnDestroy() void OnDestroy()
{ {
Settings.EnabledChange -= this.SetEnabled; Settings.EnabledChange -= this.SetEnabled;
Settings.HeadTrackingChange -= this.SetHeadTracking; Settings.HeadTrackingChange -= this.SetHeadTracking;
Settings.EyeTrackingChange -= this.SetEyeTracking; Settings.EyeTrackingChange -= this.SetEyeTracking;
Settings.BlinkingChange -= this.SetBlinking; Settings.BlinkingChange -= this.SetBlinking;
Settings.SmoothingChange -= this.SetSmoothing; Settings.SmoothingChange -= this.SetSmoothing;
Settings.MirroredChange -= this.SetMirrored; Settings.MirroredChange -= this.SetMirrored;
Settings.FaceOverrideChange -= this.SetFaceOverride; Settings.FaceOverrideChange -= this.SetFaceOverride;
} }
// Tracking updates // Tracking updates
public void UpdateTrackingData(ref TrackingData p_data) public void UpdateTrackingData(ref TrackingData p_data)
{ {
m_headPosition.Set(p_data.m_headPositionX * (m_mirrored ? -1f : 1f), p_data.m_headPositionY, p_data.m_headPositionZ); m_headPosition.Set(p_data.m_headPositionX * (m_mirrored ? -1f : 1f), p_data.m_headPositionY, p_data.m_headPositionZ);
m_headRotation.Set(p_data.m_headRotationX, p_data.m_headRotationY * (m_mirrored ? -1f : 1f), p_data.m_headRotationZ * (m_mirrored ? -1f : 1f), p_data.m_headRotationW); m_headRotation.Set(p_data.m_headRotationX, p_data.m_headRotationY * (m_mirrored ? -1f : 1f), p_data.m_headRotationZ * (m_mirrored ? -1f : 1f), p_data.m_headRotationW);
m_gazeDirection.Set(m_mirrored ? (1f - p_data.m_gazeX) : p_data.m_gazeX, p_data.m_gazeY); m_gazeDirection.Set(m_mirrored ? (1f - p_data.m_gazeX) : p_data.m_gazeX, p_data.m_gazeY);
m_blinkProgress = p_data.m_blink; m_blinkProgress = p_data.m_blink;
m_mouthShapes.Set(p_data.m_mouthOpen, p_data.m_mouthShape); m_mouthShapes.Set(p_data.m_mouthOpen, p_data.m_mouthShape);
m_eyebrowsProgress = p_data.m_brows; m_eyebrowsProgress = p_data.m_brows;
} }
void OnLookIKPostUpdate() void OnLookIKPostUpdate()
{ {
if(m_enabled && m_headTracking && (m_headBone != null)) if(m_enabled && m_headTracking && (m_headBone != null))
{ {
m_lastHeadRotation = Quaternion.Slerp(m_lastHeadRotation, m_avatarDescriptor.transform.rotation * (m_headRotation * m_bindRotation), m_smoothing); m_lastHeadRotation = Quaternion.Slerp(m_lastHeadRotation, m_avatarDescriptor.transform.rotation * (m_headRotation * m_bindRotation), m_smoothing);
if(!(bool)ms_emotePlaying.GetValue(PlayerSetup.Instance)) if(!(bool)ms_emotePlaying.GetValue(PlayerSetup.Instance))
m_headBone.rotation = m_lastHeadRotation; m_headBone.rotation = m_lastHeadRotation;
} }
} }
// Game events // Game events
internal void OnEyeControllerUpdate(CVREyeController p_component) internal void OnEyeControllerUpdate(CVREyeController p_component)
{ {
if(m_enabled) if(m_enabled)
{ {
// Gaze // Gaze
if(m_eyeTracking) if(m_eyeTracking)
{ {
Transform l_camera = PlayerSetup.Instance.GetActiveCamera().transform; Transform l_camera = PlayerSetup.Instance.GetActiveCamera().transform;
p_component.manualViewTarget = true; p_component.manualViewTarget = true;
p_component.targetViewPosition = l_camera.position + l_camera.rotation * new Vector3((m_gazeDirection.x - 0.5f) * 2f, (m_gazeDirection.y - 0.5f) * 2f, 1f); p_component.targetViewPosition = l_camera.position + l_camera.rotation * new Vector3((m_gazeDirection.x - 0.5f) * 2f, (m_gazeDirection.y - 0.5f) * 2f, 1f);
} }
// Blink // Blink
if(m_blinking) if(m_blinking)
{ {
p_component.manualBlinking = true; p_component.manualBlinking = true;
p_component.blinkProgress = m_blinkProgress; p_component.blinkProgress = m_blinkProgress;
} }
} }
} }
internal void OnFaceTrackingUpdate(CVRFaceTracking p_component) internal void OnFaceTrackingUpdate(CVRFaceTracking p_component)
{ {
if(m_enabled && m_faceOverride) if(m_enabled && m_faceOverride)
{ {
if(m_avatarDescriptor != null) if(m_avatarDescriptor != null)
m_avatarDescriptor.useVisemeLipsync = false; m_avatarDescriptor.useVisemeLipsync = false;
float l_weight = Mathf.Clamp01(Mathf.InverseLerp(0.25f, 1f, Mathf.Abs(m_mouthShapes.y))) * 100f; float l_weight = Mathf.Clamp01(Mathf.InverseLerp(0.25f, 1f, Mathf.Abs(m_mouthShapes.y))) * 100f;
p_component.BlendShapeValues[(int)LipShape_v2.Jaw_Open] = m_mouthShapes.x * 100f; p_component.BlendShapeValues[(int)LipShape_v2.Jaw_Open] = m_mouthShapes.x * 100f;
p_component.BlendShapeValues[(int)LipShape_v2.Mouth_Pout] = ((m_mouthShapes.y > 0f) ? l_weight : 0f); p_component.BlendShapeValues[(int)LipShape_v2.Mouth_Pout] = ((m_mouthShapes.y > 0f) ? l_weight : 0f);
p_component.BlendShapeValues[(int)LipShape_v2.Mouth_Smile_Left] = ((m_mouthShapes.y < 0f) ? l_weight : 0f); p_component.BlendShapeValues[(int)LipShape_v2.Mouth_Smile_Left] = ((m_mouthShapes.y < 0f) ? l_weight : 0f);
p_component.BlendShapeValues[(int)LipShape_v2.Mouth_Smile_Right] = ((m_mouthShapes.y < 0f) ? l_weight : 0f); p_component.BlendShapeValues[(int)LipShape_v2.Mouth_Smile_Right] = ((m_mouthShapes.y < 0f) ? l_weight : 0f);
p_component.LipSyncWasUpdated = true; p_component.LipSyncWasUpdated = true;
p_component.UpdateLipShapes(); p_component.UpdateLipShapes();
} }
} }
internal void OnSetupAvatar() internal void OnSetupAvatar()
{ {
m_avatarDescriptor = PlayerSetup.Instance._avatar.GetComponent<CVRAvatar>(); m_avatarDescriptor = PlayerSetup.Instance._avatar.GetComponent<CVRAvatar>();
m_headBone = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Head); m_headBone = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Head);
m_lookIK = PlayerSetup.Instance._avatar.GetComponent<LookAtIK>(); m_lookIK = PlayerSetup.Instance._avatar.GetComponent<LookAtIK>();
if(m_headBone != null) if(m_headBone != null)
m_bindRotation = (m_avatarDescriptor.transform.GetMatrix().inverse * m_headBone.GetMatrix()).rotation; m_bindRotation = (m_avatarDescriptor.transform.GetMatrix().inverse * m_headBone.GetMatrix()).rotation;
if(m_lookIK != null) if(m_lookIK != null)
m_lookIK.solver.OnPostUpdate += this.OnLookIKPostUpdate; m_lookIK.solver.OnPostUpdate += this.OnLookIKPostUpdate;
} }
internal void OnAvatarClear() internal void OnAvatarClear()
{ {
m_avatarDescriptor = null; m_avatarDescriptor = null;
m_lookIK = null; m_lookIK = null;
m_headBone = null; m_headBone = null;
m_lastHeadRotation = Quaternion.identity; m_lastHeadRotation = Quaternion.identity;
m_bindRotation = Quaternion.identity; m_bindRotation = Quaternion.identity;
} }
// Settings // Settings
internal void SetEnabled(bool p_state) internal void SetEnabled(bool p_state)
{ {
if(m_enabled != p_state) if(m_enabled != p_state)
{ {
m_enabled = p_state; m_enabled = p_state;
if(m_enabled && m_headTracking) if(m_enabled && m_headTracking)
m_lastHeadRotation = ((m_headBone != null) ? m_headBone.rotation : m_bindRotation); m_lastHeadRotation = ((m_headBone != null) ? m_headBone.rotation : m_bindRotation);
} }
} }
internal void SetHeadTracking(bool p_state) internal void SetHeadTracking(bool p_state)
{ {
if(m_headTracking != p_state) if(m_headTracking != p_state)
{ {
m_headTracking = p_state; m_headTracking = p_state;
if(m_enabled && m_headTracking) if(m_enabled && m_headTracking)
m_lastHeadRotation = ((m_headBone != null) ? m_headBone.rotation : m_bindRotation); m_lastHeadRotation = ((m_headBone != null) ? m_headBone.rotation : m_bindRotation);
} }
} }
internal void SetEyeTracking(bool p_state) internal void SetEyeTracking(bool p_state)
{ {
m_eyeTracking = p_state; m_eyeTracking = p_state;
} }
internal void SetBlinking(bool p_state) internal void SetBlinking(bool p_state)
{ {
m_blinking = p_state; m_blinking = p_state;
} }
internal void SetSmoothing(float p_value) internal void SetSmoothing(float p_value)
{ {
m_smoothing = 1f - Mathf.Clamp(p_value, 0f, 0.99f); m_smoothing = 1f - Mathf.Clamp(p_value, 0f, 0.99f);
} }
internal void SetMirrored(bool p_state) internal void SetMirrored(bool p_state)
{ {
m_mirrored = p_state; m_mirrored = p_state;
} }
internal void SetFaceOverride(bool p_state) internal void SetFaceOverride(bool p_state)
{ {
m_faceOverride = p_state; m_faceOverride = p_state;
} }
} }
} }

View file

@ -1,146 +1,146 @@
using ABI.CCK.Components; using ABI.CCK.Components;
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using System.Reflection; using System.Reflection;
namespace ml_dht namespace ml_dht
{ {
public class DesktopHeadTracking : MelonLoader.MelonMod public class DesktopHeadTracking : MelonLoader.MelonMod
{ {
static DesktopHeadTracking ms_instance = null; static DesktopHeadTracking ms_instance = null;
MemoryMapReader m_mapReader = null; MemoryMapReader m_mapReader = null;
byte[] m_buffer = null; byte[] m_buffer = null;
TrackingData m_trackingData; TrackingData m_trackingData;
HeadTracked m_localTracked = null; HeadTracked m_localTracked = null;
public override void OnInitializeMelon() public override void OnInitializeMelon()
{ {
if(ms_instance == null) if(ms_instance == null)
ms_instance = this; ms_instance = this;
Settings.Init(); Settings.Init();
m_mapReader = new MemoryMapReader(); m_mapReader = new MemoryMapReader();
m_buffer = new byte[1024]; m_buffer = new byte[1024];
m_mapReader.Open("head/data"); m_mapReader.Open("head/data");
// Patches // Patches
HarmonyInstance.Patch( HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)),
null, null,
new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
); );
HarmonyInstance.Patch( HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)), typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)),
null, null,
new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
); );
HarmonyInstance.Patch( HarmonyInstance.Patch(
typeof(CVREyeController).GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic), typeof(CVREyeController).GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic),
null, null,
new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnEyeControllerUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnEyeControllerUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
); );
HarmonyInstance.Patch( HarmonyInstance.Patch(
typeof(CVRFaceTracking).GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic), typeof(CVRFaceTracking).GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic),
null, null,
new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnFaceTrackingUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) new HarmonyLib.HarmonyMethod(typeof(DesktopHeadTracking).GetMethod(nameof(OnFaceTrackingUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
); );
MelonLoader.MelonCoroutines.Start(WaitForPlayer()); MelonLoader.MelonCoroutines.Start(WaitForPlayer());
} }
System.Collections.IEnumerator WaitForPlayer() System.Collections.IEnumerator WaitForPlayer()
{ {
while(PlayerSetup.Instance == null) while(PlayerSetup.Instance == null)
yield return null; yield return null;
m_localTracked = PlayerSetup.Instance.gameObject.AddComponent<HeadTracked>(); m_localTracked = PlayerSetup.Instance.gameObject.AddComponent<HeadTracked>();
m_localTracked.SetEnabled(Settings.Enabled); m_localTracked.SetEnabled(Settings.Enabled);
m_localTracked.SetHeadTracking(Settings.HeadTracking); m_localTracked.SetHeadTracking(Settings.HeadTracking);
m_localTracked.SetEyeTracking(Settings.EyeTracking); m_localTracked.SetEyeTracking(Settings.EyeTracking);
m_localTracked.SetBlinking(Settings.Blinking); m_localTracked.SetBlinking(Settings.Blinking);
m_localTracked.SetMirrored(Settings.Mirrored); m_localTracked.SetMirrored(Settings.Mirrored);
m_localTracked.SetSmoothing(Settings.Smoothing); m_localTracked.SetSmoothing(Settings.Smoothing);
m_localTracked.SetFaceOverride(Settings.FaceOverride); m_localTracked.SetFaceOverride(Settings.FaceOverride);
} }
public override void OnDeinitializeMelon() public override void OnDeinitializeMelon()
{ {
if(ms_instance == this) if(ms_instance == this)
ms_instance = null; ms_instance = null;
m_mapReader?.Close(); m_mapReader?.Close();
m_mapReader = null; m_mapReader = null;
m_buffer = null; m_buffer = null;
m_localTracked = null; m_localTracked = null;
} }
public override void OnUpdate() public override void OnUpdate()
{ {
if(Settings.Enabled && m_mapReader.Read(ref m_buffer)) if(Settings.Enabled && m_mapReader.Read(ref m_buffer))
{ {
m_trackingData = TrackingData.ToObject(m_buffer); m_trackingData = TrackingData.ToObject(m_buffer);
if(m_localTracked != null) if(m_localTracked != null)
m_localTracked.UpdateTrackingData(ref m_trackingData); m_localTracked.UpdateTrackingData(ref m_trackingData);
} }
} }
static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar(); static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar();
void OnSetupAvatar() void OnSetupAvatar()
{ {
try try
{ {
if(m_localTracked != null) if(m_localTracked != null)
m_localTracked.OnSetupAvatar(); m_localTracked.OnSetupAvatar();
} }
catch(System.Exception e) catch(System.Exception e)
{ {
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear(); static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear();
void OnAvatarClear() void OnAvatarClear()
{ {
try try
{ {
if(m_localTracked != null) if(m_localTracked != null)
m_localTracked.OnAvatarClear(); m_localTracked.OnAvatarClear();
} }
catch(System.Exception e) catch(System.Exception e)
{ {
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
static void OnEyeControllerUpdate_Postfix(ref CVREyeController __instance) => ms_instance?.OnEyeControllerUpdate(__instance); static void OnEyeControllerUpdate_Postfix(ref CVREyeController __instance) => ms_instance?.OnEyeControllerUpdate(__instance);
void OnEyeControllerUpdate(CVREyeController p_component) void OnEyeControllerUpdate(CVREyeController p_component)
{ {
try try
{ {
if(p_component.isLocal && (m_localTracked != null)) if(p_component.isLocal && (m_localTracked != null))
m_localTracked.OnEyeControllerUpdate(p_component); m_localTracked.OnEyeControllerUpdate(p_component);
} }
catch(System.Exception e) catch(System.Exception e)
{ {
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
static void OnFaceTrackingUpdate_Postfix(ref CVRFaceTracking __instance) => ms_instance?.OnFaceTrackingUpdate(__instance); static void OnFaceTrackingUpdate_Postfix(ref CVRFaceTracking __instance) => ms_instance?.OnFaceTrackingUpdate(__instance);
void OnFaceTrackingUpdate(CVRFaceTracking p_component) void OnFaceTrackingUpdate(CVRFaceTracking p_component)
{ {
try try
{ {
if(p_component.isLocal && (m_localTracked != null)) if(p_component.isLocal && (m_localTracked != null))
m_localTracked.OnFaceTrackingUpdate(p_component); m_localTracked.OnFaceTrackingUpdate(p_component);
} }
catch(System.Exception e) catch(System.Exception e)
{ {
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
} }
} }

View file

@ -1,4 +1,4 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_dht.DesktopHeadTracking), "DesktopHeadTracking", "1.1.2-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonInfo(typeof(ml_dht.DesktopHeadTracking), "DesktopHeadTracking", "1.1.2-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]

View file

@ -1,29 +1,29 @@
# Desktop Head Tracking # Desktop Head Tracking
This mod adds desktop head tracking based on data from memory-mapped file `head/data`. This mod adds desktop head tracking based on data from memory-mapped file `head/data`.
Refer to `TrackingData.cs` for reference in case of implementing own software. Refer to `TrackingData.cs` for reference in case of implementing own software.
[![](.github/img_01.png)](https://youtu.be/jgcFhSNi9DM) [![](.github/img_01.png)](https://youtu.be/jgcFhSNi9DM)
# Features # Features
* Head rotation * Head rotation
* Eyes gaze direction * Eyes gaze direction
* Blinking * Blinking
* Basic mouth shapes * Basic mouth shapes
# Installation # Installation
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) * Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
* Get [latest release DLL](../../../releases/latest): * Get [latest release DLL](../../../releases/latest):
* Put `ml_dht.dll` in `Mods` folder of game * Put `ml_dht.dll` in `Mods` folder of game
# Usage # Usage
Available mod's settings in `Settings - Implementation - Desktop Head Tracking`: Available mod's settings in `Settings - Implementation - Desktop Head Tracking`:
* **Enabled:** enables mod's activity; default value - `false`. * **Enabled:** enables mod's activity; default value - `false`.
* **Use head tracking:** enables head tracking; default value - `true`. * **Use head tracking:** enables head tracking; default value - `true`.
* **Use eyes tracking:** uses eyes tracking from data; default value - `true`. * **Use eyes tracking:** uses eyes tracking from data; default value - `true`.
* **Use blinking:** uses blinking from data; default value - `true`. * **Use blinking:** uses blinking from data; default value - `true`.
* **Mirrored movement:** mirrors movement and gaze along 0YZ plane; default value - `false`. * **Mirrored movement:** mirrors movement and gaze along 0YZ plane; default value - `false`.
* **Movement smoothing:** smoothing factor between new and old movement data; default value - `50`. * **Movement smoothing:** smoothing factor between new and old movement data; default value - `50`.
* **Override face tracking:** overrides and activates avatar's `VRC Face Tracking` components. List of used shapes: `Jaw_Open`, `Mouth_Pout`, `Mouth_Smile_Left`, `Mouth_Smile_Right`; default value - `true`. * **Override face tracking:** overrides and activates avatar's `VRC Face Tracking` components. List of used shapes: `Jaw_Open`, `Mouth_Pout`, `Mouth_Smile_Left`, `Mouth_Smile_Right`; default value - `true`.
# Known compatible tracking software # Known compatible tracking software
* [VSeeFace](https://www.vseeface.icu) with [Tracking Data Parser mod](https://github.com/SDraw/ml_mods_vsf) * [VSeeFace](https://www.vseeface.icu) with [Tracking Data Parser mod](https://github.com/SDraw/ml_mods_vsf)

View file

@ -1,26 +1,26 @@
using System; using System;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
namespace ml_dht namespace ml_dht
{ {
static class Scripts static class Scripts
{ {
public static string GetEmbeddedScript(string p_name) public static string GetEmbeddedScript(string p_name)
{ {
string l_result = ""; string l_result = "";
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name; string l_assemblyName = l_assembly.GetName().Name;
try try
{ {
Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); Stream l_libraryStream = l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name);
StreamReader l_streadReader = new StreamReader(l_libraryStream); StreamReader l_streadReader = new StreamReader(l_libraryStream);
l_result = l_streadReader.ReadToEnd(); l_result = l_streadReader.ReadToEnd();
} }
catch(Exception) { } catch(Exception) { }
return l_result; return l_result;
} }
} }
} }

View file

@ -1,163 +1,163 @@
using ABI_RC.Core.InteractionSystem; using ABI_RC.Core.InteractionSystem;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace ml_dht namespace ml_dht
{ {
static class Settings static class Settings
{ {
enum ModSetting enum ModSetting
{ {
Enabled = 0, Enabled = 0,
HeadTracking, HeadTracking,
EyeTracking, EyeTracking,
Blinking, Blinking,
Mirrored, Mirrored,
Smoothing, Smoothing,
FaceOverride FaceOverride
} }
public static bool Enabled { get; private set; } = false; public static bool Enabled { get; private set; } = false;
public static bool HeadTracking { get; private set; } = true; public static bool HeadTracking { get; private set; } = true;
public static bool EyeTracking { get; private set; } = true; public static bool EyeTracking { get; private set; } = true;
public static bool Blinking { get; private set; } = true; public static bool Blinking { get; private set; } = true;
public static bool Mirrored { get; private set; } = false; public static bool Mirrored { get; private set; } = false;
public static float Smoothing { get; private set; } = 0.5f; public static float Smoothing { get; private set; } = 0.5f;
public static bool FaceOverride { get; private set; } = true; public static bool FaceOverride { get; private set; } = true;
static MelonLoader.MelonPreferences_Category ms_category = null; static MelonLoader.MelonPreferences_Category ms_category = null;
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null; static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
static public event Action<bool> EnabledChange; static public event Action<bool> EnabledChange;
static public event Action<bool> HeadTrackingChange; static public event Action<bool> HeadTrackingChange;
static public event Action<bool> EyeTrackingChange; static public event Action<bool> EyeTrackingChange;
static public event Action<bool> BlinkingChange; static public event Action<bool> BlinkingChange;
static public event Action<bool> MirroredChange; static public event Action<bool> MirroredChange;
static public event Action<float> SmoothingChange; static public event Action<float> SmoothingChange;
static public event Action<bool> FaceOverrideChange; static public event Action<bool> FaceOverrideChange;
internal static void Init() internal static void Init()
{ {
ms_category = MelonLoader.MelonPreferences.CreateCategory("DHT"); ms_category = MelonLoader.MelonPreferences.CreateCategory("DHT");
ms_entries = new List<MelonLoader.MelonPreferences_Entry>() ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
{ {
ms_category.CreateEntry(ModSetting.Enabled.ToString(), Enabled), ms_category.CreateEntry(ModSetting.Enabled.ToString(), Enabled),
ms_category.CreateEntry(ModSetting.HeadTracking.ToString(), HeadTracking), ms_category.CreateEntry(ModSetting.HeadTracking.ToString(), HeadTracking),
ms_category.CreateEntry(ModSetting.EyeTracking.ToString(), EyeTracking), ms_category.CreateEntry(ModSetting.EyeTracking.ToString(), EyeTracking),
ms_category.CreateEntry(ModSetting.Blinking.ToString(), Blinking), ms_category.CreateEntry(ModSetting.Blinking.ToString(), Blinking),
ms_category.CreateEntry(ModSetting.Mirrored.ToString(), Mirrored), ms_category.CreateEntry(ModSetting.Mirrored.ToString(), Mirrored),
ms_category.CreateEntry(ModSetting.Smoothing.ToString(), (int)(Smoothing * 50f)), ms_category.CreateEntry(ModSetting.Smoothing.ToString(), (int)(Smoothing * 50f)),
ms_category.CreateEntry(ModSetting.FaceOverride.ToString(), FaceOverride) ms_category.CreateEntry(ModSetting.FaceOverride.ToString(), FaceOverride)
}; };
Load(); Load();
MelonLoader.MelonCoroutines.Start(WaitMainMenuUi()); MelonLoader.MelonCoroutines.Start(WaitMainMenuUi());
} }
static System.Collections.IEnumerator WaitMainMenuUi() static System.Collections.IEnumerator WaitMainMenuUi()
{ {
while(ViewManager.Instance == null) while(ViewManager.Instance == null)
yield return null; yield return null;
while(ViewManager.Instance.gameMenuView == null) while(ViewManager.Instance.gameMenuView == null)
yield return null; yield return null;
while(ViewManager.Instance.gameMenuView.Listener == null) while(ViewManager.Instance.gameMenuView.Listener == null)
yield return null; yield return null;
ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () => ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () =>
{ {
ViewManager.Instance.gameMenuView.View.BindCall("MelonMod_DHT_Call_InpSlider", new Action<string, string>(OnSliderUpdate)); ViewManager.Instance.gameMenuView.View.BindCall("MelonMod_DHT_Call_InpSlider", new Action<string, string>(OnSliderUpdate));
ViewManager.Instance.gameMenuView.View.BindCall("MelonMod_DHT_Call_InpToggle", new Action<string, string>(OnToggleUpdate)); ViewManager.Instance.gameMenuView.View.BindCall("MelonMod_DHT_Call_InpToggle", new Action<string, string>(OnToggleUpdate));
}; };
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
{ {
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("menu.js")); ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("menu.js"));
foreach(var l_entry in ms_entries) foreach(var l_entry in ms_entries)
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSettingDHT", l_entry.DisplayName, l_entry.GetValueAsString()); ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSettingDHT", l_entry.DisplayName, l_entry.GetValueAsString());
}; };
} }
static void Load() static void Load()
{ {
Enabled = (bool)ms_entries[(int)ModSetting.Enabled].BoxedValue; Enabled = (bool)ms_entries[(int)ModSetting.Enabled].BoxedValue;
HeadTracking = (bool)ms_entries[(int)ModSetting.HeadTracking].BoxedValue; HeadTracking = (bool)ms_entries[(int)ModSetting.HeadTracking].BoxedValue;
EyeTracking = (bool)ms_entries[(int)ModSetting.EyeTracking].BoxedValue; EyeTracking = (bool)ms_entries[(int)ModSetting.EyeTracking].BoxedValue;
Blinking = (bool)ms_entries[(int)ModSetting.Blinking].BoxedValue; Blinking = (bool)ms_entries[(int)ModSetting.Blinking].BoxedValue;
Mirrored = (bool)ms_entries[(int)ModSetting.Mirrored].BoxedValue; Mirrored = (bool)ms_entries[(int)ModSetting.Mirrored].BoxedValue;
Smoothing = ((int)ms_entries[(int)ModSetting.Smoothing].BoxedValue) * 0.01f; Smoothing = ((int)ms_entries[(int)ModSetting.Smoothing].BoxedValue) * 0.01f;
FaceOverride = (bool)ms_entries[(int)ModSetting.FaceOverride].BoxedValue; FaceOverride = (bool)ms_entries[(int)ModSetting.FaceOverride].BoxedValue;
} }
static void OnSliderUpdate(string p_name, string p_value) static void OnSliderUpdate(string p_name, string p_value)
{ {
if(Enum.TryParse(p_name, out ModSetting l_setting)) if(Enum.TryParse(p_name, out ModSetting l_setting))
{ {
switch(l_setting) switch(l_setting)
{ {
case ModSetting.Smoothing: case ModSetting.Smoothing:
{ {
Smoothing = int.Parse(p_value) * 0.01f; Smoothing = int.Parse(p_value) * 0.01f;
SmoothingChange?.Invoke(Smoothing); SmoothingChange?.Invoke(Smoothing);
} }
break; break;
} }
ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value); ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value);
} }
} }
static void OnToggleUpdate(string p_name, string p_value) static void OnToggleUpdate(string p_name, string p_value)
{ {
if(Enum.TryParse(p_name, out ModSetting l_setting)) if(Enum.TryParse(p_name, out ModSetting l_setting))
{ {
switch(l_setting) switch(l_setting)
{ {
case ModSetting.Enabled: case ModSetting.Enabled:
{ {
Enabled = bool.Parse(p_value); Enabled = bool.Parse(p_value);
EnabledChange?.Invoke(Enabled); EnabledChange?.Invoke(Enabled);
} }
break; break;
case ModSetting.HeadTracking: case ModSetting.HeadTracking:
{ {
HeadTracking = bool.Parse(p_value); HeadTracking = bool.Parse(p_value);
HeadTrackingChange?.Invoke(HeadTracking); HeadTrackingChange?.Invoke(HeadTracking);
} }
break; break;
case ModSetting.EyeTracking: case ModSetting.EyeTracking:
{ {
EyeTracking = bool.Parse(p_value); EyeTracking = bool.Parse(p_value);
EyeTrackingChange?.Invoke(EyeTracking); EyeTrackingChange?.Invoke(EyeTracking);
} }
break; break;
case ModSetting.Blinking: case ModSetting.Blinking:
{ {
Blinking = bool.Parse(p_value); Blinking = bool.Parse(p_value);
BlinkingChange?.Invoke(Blinking); BlinkingChange?.Invoke(Blinking);
} }
break; break;
case ModSetting.Mirrored: case ModSetting.Mirrored:
{ {
Mirrored = bool.Parse(p_value); Mirrored = bool.Parse(p_value);
MirroredChange?.Invoke(Mirrored); MirroredChange?.Invoke(Mirrored);
} }
break; break;
case ModSetting.FaceOverride: case ModSetting.FaceOverride:
{ {
FaceOverride = bool.Parse(p_value); FaceOverride = bool.Parse(p_value);
FaceOverrideChange?.Invoke(FaceOverride); FaceOverrideChange?.Invoke(FaceOverride);
} }
break; break;
} }
ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value); ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);
} }
} }
} }
} }

View file

@ -1,18 +1,18 @@
using ABI_RC.Core.UI; using ABI_RC.Core.UI;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
namespace ml_dht namespace ml_dht
{ {
static class Utils static class Utils
{ {
static FieldInfo ms_cohtmlView = typeof(CohtmlControlledViewDisposable).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); static FieldInfo ms_cohtmlView = typeof(CohtmlControlledViewDisposable).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance);
public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false) 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); 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); public static void ExecuteScript(this CohtmlControlledViewDisposable p_viewDisposable, string p_script) => ((cohtml.Net.View)ms_cohtmlView.GetValue(p_viewDisposable))?.ExecuteScript(p_script);
} }
} }

View file

@ -1,71 +1,71 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<PackageId>DesktopHeadTracking</PackageId> <PackageId>DesktopHeadTracking</PackageId>
<Authors>SDraw</Authors> <Authors>SDraw</Authors>
<Company>None</Company> <Company>None</Company>
<Product>DesktopHeadTracking</Product> <Product>DesktopHeadTracking</Product>
<Version>1.1.2</Version> <Version>1.1.2</Version>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<DebugType>none</DebugType> <DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols> <DebugSymbols>false</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="DesktopHeadTracking.json" /> <None Remove="DesktopHeadTracking.json" />
<None Remove="resources\menu.js" /> <None Remove="resources\menu.js" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="resources\menu.js" /> <EmbeddedResource Include="resources\menu.js" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="0Harmony"> <Reference Include="0Harmony">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\0Harmony.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\0Harmony.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="Assembly-CSharp"> <Reference Include="Assembly-CSharp">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="Assembly-CSharp-firstpass"> <Reference Include="Assembly-CSharp-firstpass">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="cohtml.Net"> <Reference Include="cohtml.Net">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="Cohtml.Runtime"> <Reference Include="Cohtml.Runtime">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="MelonLoader"> <Reference Include="MelonLoader">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\MelonLoader.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\MelonLoader.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="UnityEngine"> <Reference Include="UnityEngine">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.AnimationModule"> <Reference Include="UnityEngine.AnimationModule">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.CoreModule"> <Reference Include="UnityEngine.CoreModule">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="copy /y &quot;$(TargetPath)&quot; &quot;D:\Games\Steam\steamapps\common\ChilloutVR\Mods\&quot;" /> <Exec Command="copy /y &quot;$(TargetPath)&quot; &quot;D:\Games\Steam\steamapps\common\ChilloutVR\Mods\&quot;" />
</Target> </Target>
</Project> </Project>

View file

@ -1,245 +1,245 @@
// Add settings // Add settings
var g_modSettingsDHT = []; var g_modSettingsDHT = [];
engine.on('updateModSettingDHT', function (_name, _value) { engine.on('updateModSettingDHT', function (_name, _value) {
for (var i = 0; i < g_modSettingsDHT.length; i++) { for (var i = 0; i < g_modSettingsDHT.length; i++) {
if (g_modSettingsDHT[i].name == _name) { if (g_modSettingsDHT[i].name == _name) {
g_modSettingsDHT[i].updateValue(_value); g_modSettingsDHT[i].updateValue(_value);
break; break;
} }
} }
}); });
// Modified from original `inp` types, because I have no js knowledge to hook stuff // Modified from original `inp` types, because I have no js knowledge to hook stuff
function inp_slider_mod_dht(_obj, _callbackName) { function inp_slider_mod_dht(_obj, _callbackName) {
this.obj = _obj; this.obj = _obj;
this.callbackName = _callbackName; this.callbackName = _callbackName;
this.minValue = parseFloat(_obj.getAttribute('data-min')); this.minValue = parseFloat(_obj.getAttribute('data-min'));
this.maxValue = parseFloat(_obj.getAttribute('data-max')); this.maxValue = parseFloat(_obj.getAttribute('data-max'));
this.percent = 0; this.percent = 0;
this.value = parseFloat(_obj.getAttribute('data-current')); this.value = parseFloat(_obj.getAttribute('data-current'));
this.dragActive = false; this.dragActive = false;
this.name = _obj.id; this.name = _obj.id;
this.type = _obj.getAttribute('data-type'); this.type = _obj.getAttribute('data-type');
this.stepSize = _obj.getAttribute('data-stepSize') || 0; this.stepSize = _obj.getAttribute('data-stepSize') || 0;
this.format = _obj.getAttribute('data-format') || '{value}'; this.format = _obj.getAttribute('data-format') || '{value}';
var self = this; var self = this;
if (this.stepSize != 0) if (this.stepSize != 0)
this.value = Math.round(this.value / this.stepSize) * this.stepSize; this.value = Math.round(this.value / this.stepSize) * this.stepSize;
else else
this.value = Math.round(this.value); this.value = Math.round(this.value);
this.valueLabelBackground = document.createElement('div'); this.valueLabelBackground = document.createElement('div');
this.valueLabelBackground.className = 'valueLabel background'; this.valueLabelBackground.className = 'valueLabel background';
this.valueLabelBackground.innerHTML = this.format.replace('{value}', this.value); this.valueLabelBackground.innerHTML = this.format.replace('{value}', this.value);
this.obj.appendChild(this.valueLabelBackground); this.obj.appendChild(this.valueLabelBackground);
this.valueBar = document.createElement('div'); this.valueBar = document.createElement('div');
this.valueBar.className = 'valueBar'; this.valueBar.className = 'valueBar';
this.valueBar.setAttribute('style', 'width: ' + (((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;'); this.valueBar.setAttribute('style', 'width: ' + (((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
this.obj.appendChild(this.valueBar); this.obj.appendChild(this.valueBar);
this.valueLabelForeground = document.createElement('div'); this.valueLabelForeground = document.createElement('div');
this.valueLabelForeground.className = 'valueLabel foreground'; this.valueLabelForeground.className = 'valueLabel foreground';
this.valueLabelForeground.innerHTML = this.format.replace('{value}', this.value); this.valueLabelForeground.innerHTML = this.format.replace('{value}', this.value);
this.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / ((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;'); this.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / ((this.value - this.minValue) / (this.maxValue - this.minValue)) * 100) + '%;');
this.valueBar.appendChild(this.valueLabelForeground); this.valueBar.appendChild(this.valueLabelForeground);
this.mouseDown = function (_e) { this.mouseDown = function (_e) {
self.dragActive = true; self.dragActive = true;
self.mouseMove(_e, false); self.mouseMove(_e, false);
} }
this.mouseMove = function (_e, _write) { this.mouseMove = function (_e, _write) {
if (self.dragActive) { if (self.dragActive) {
var rect = _obj.getBoundingClientRect(); var rect = _obj.getBoundingClientRect();
var start = rect.left; var start = rect.left;
var end = rect.right; var end = rect.right;
self.percent = Math.min(Math.max((_e.clientX - start) / rect.width, 0), 1); self.percent = Math.min(Math.max((_e.clientX - start) / rect.width, 0), 1);
var value = self.percent; var value = self.percent;
value *= (self.maxValue - self.minValue); value *= (self.maxValue - self.minValue);
value += self.minValue; value += self.minValue;
if (self.stepSize != 0) { if (self.stepSize != 0) {
value = Math.round(value / self.stepSize); value = Math.round(value / self.stepSize);
self.value = value * self.stepSize; self.value = value * self.stepSize;
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue); self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
} }
else else
self.value = Math.round(value); self.value = Math.round(value);
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;'); self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;'); self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value); self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
engine.call(self.callbackName, self.name, "" + self.value); engine.call(self.callbackName, self.name, "" + self.value);
self.displayImperial(); self.displayImperial();
} }
} }
this.mouseUp = function (_e) { this.mouseUp = function (_e) {
self.mouseMove(_e, true); self.mouseMove(_e, true);
self.dragActive = false; self.dragActive = false;
} }
_obj.addEventListener('mousedown', this.mouseDown); _obj.addEventListener('mousedown', this.mouseDown);
document.addEventListener('mousemove', this.mouseMove); document.addEventListener('mousemove', this.mouseMove);
document.addEventListener('mouseup', this.mouseUp); document.addEventListener('mouseup', this.mouseUp);
this.getValue = function () { this.getValue = function () {
return self.value; return self.value;
} }
this.updateValue = function (value) { this.updateValue = function (value) {
if (self.stepSize != 0) if (self.stepSize != 0)
self.value = Math.round(value * self.stepSize) / self.stepSize; self.value = Math.round(value * self.stepSize) / self.stepSize;
else else
self.value = Math.round(value); self.value = Math.round(value);
self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue); self.percent = (self.value - self.minValue) / (self.maxValue - self.minValue);
self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;'); self.valueBar.setAttribute('style', 'width: ' + (self.percent * 100) + '%;');
self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;'); self.valueLabelForeground.setAttribute('style', 'width: ' + (1.0 / self.percent * 100) + '%;');
self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value); self.valueLabelBackground.innerHTML = self.valueLabelForeground.innerHTML = self.format.replace('{value}', self.value);
self.displayImperial(); self.displayImperial();
} }
this.displayImperial = function () { this.displayImperial = function () {
var displays = document.querySelectorAll('.imperialDisplay'); var displays = document.querySelectorAll('.imperialDisplay');
for (var i = 0; i < displays.length; i++) { for (var i = 0; i < displays.length; i++) {
var binding = displays[i].getAttribute('data-binding'); var binding = displays[i].getAttribute('data-binding');
if (binding == self.name) { if (binding == self.name) {
var realFeet = ((self.value * 0.393700) / 12); var realFeet = ((self.value * 0.393700) / 12);
var feet = Math.floor(realFeet); var feet = Math.floor(realFeet);
var inches = Math.floor((realFeet - feet) * 12); var inches = Math.floor((realFeet - feet) * 12);
displays[i].innerHTML = feet + "&apos;" + inches + '&apos;&apos;'; displays[i].innerHTML = feet + "&apos;" + inches + '&apos;&apos;';
} }
} }
} }
return { return {
name: this.name, name: this.name,
value: this.getValue, value: this.getValue,
updateValue: this.updateValue updateValue: this.updateValue
} }
} }
// Modified from original `inp` types, because I have no js knowledge to hook stuff // Modified from original `inp` types, because I have no js knowledge to hook stuff
function inp_toggle_mod_dht(_obj, _callbackName) { function inp_toggle_mod_dht(_obj, _callbackName) {
this.obj = _obj; this.obj = _obj;
this.callbackName = _callbackName; this.callbackName = _callbackName;
this.value = _obj.getAttribute('data-current'); this.value = _obj.getAttribute('data-current');
this.name = _obj.id; this.name = _obj.id;
this.type = _obj.getAttribute('data-type'); this.type = _obj.getAttribute('data-type');
var self = this; var self = this;
this.mouseDown = function (_e) { this.mouseDown = function (_e) {
self.value = self.value == "True" ? "False" : "True"; self.value = self.value == "True" ? "False" : "True";
self.updateState(); self.updateState();
} }
this.updateState = function () { this.updateState = function () {
self.obj.classList.remove("checked"); self.obj.classList.remove("checked");
if (self.value == "True") { if (self.value == "True") {
self.obj.classList.add("checked"); self.obj.classList.add("checked");
} }
engine.call(self.callbackName, self.name, self.value); engine.call(self.callbackName, self.name, self.value);
} }
_obj.addEventListener('mousedown', this.mouseDown); _obj.addEventListener('mousedown', this.mouseDown);
this.getValue = function () { this.getValue = function () {
return self.value; return self.value;
} }
this.updateValue = function (value) { this.updateValue = function (value) {
self.value = value; self.value = value;
self.obj.classList.remove("checked"); self.obj.classList.remove("checked");
if (self.value == "True") { if (self.value == "True") {
self.obj.classList.add("checked"); self.obj.classList.add("checked");
} }
} }
this.updateValue(this.value); this.updateValue(this.value);
return { return {
name: this.name, name: this.name,
value: this.getValue, value: this.getValue,
updateValue: this.updateValue updateValue: this.updateValue
} }
} }
// Add own menu // Add own menu
{ {
let l_block = document.createElement('div'); let l_block = document.createElement('div');
l_block.innerHTML = ` l_block.innerHTML = `
<div class ="settings-subcategory"> <div class ="settings-subcategory">
<div class ="subcategory-name">Desktop Head Tracking</div> <div class ="subcategory-name">Desktop Head Tracking</div>
<div class ="subcategory-description"></div> <div class ="subcategory-description"></div>
</div> </div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Enabled: </div> <div class ="option-caption">Enabled: </div>
<div class ="option-input"> <div class ="option-input">
<div id="Enabled" class ="inp_toggle no-scroll" data-current="false"></div> <div id="Enabled" class ="inp_toggle no-scroll" data-current="false"></div>
</div> </div>
</div> </div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Use head tracking: </div> <div class ="option-caption">Use head tracking: </div>
<div class ="option-input"> <div class ="option-input">
<div id="HeadTracking" class ="inp_toggle no-scroll" data-current="true"></div> <div id="HeadTracking" class ="inp_toggle no-scroll" data-current="true"></div>
</div> </div>
</div> </div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Use eyes tracking: </div> <div class ="option-caption">Use eyes tracking: </div>
<div class ="option-input"> <div class ="option-input">
<div id="EyeTracking" class ="inp_toggle no-scroll" data-current="true"></div> <div id="EyeTracking" class ="inp_toggle no-scroll" data-current="true"></div>
</div> </div>
</div> </div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Use blinking: </div> <div class ="option-caption">Use blinking: </div>
<div class ="option-input"> <div class ="option-input">
<div id="Blinking" class ="inp_toggle no-scroll" data-current="true"></div> <div id="Blinking" class ="inp_toggle no-scroll" data-current="true"></div>
</div> </div>
</div> </div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Mirrored movement: </div> <div class ="option-caption">Mirrored movement: </div>
<div class ="option-input"> <div class ="option-input">
<div id="Mirrored" class ="inp_toggle no-scroll" data-current="false"></div> <div id="Mirrored" class ="inp_toggle no-scroll" data-current="false"></div>
</div> </div>
</div> </div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Movement smoothing: </div> <div class ="option-caption">Movement smoothing: </div>
<div class ="option-input"> <div class ="option-input">
<div id="Smoothing" class ="inp_slider no-scroll" data-min="0" data-max="99" data-current="50"></div> <div id="Smoothing" class ="inp_slider no-scroll" data-min="0" data-max="99" data-current="50"></div>
</div> </div>
</div> </div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Override face tracking: </div> <div class ="option-caption">Override face tracking: </div>
<div class ="option-input"> <div class ="option-input">
<div id="FaceOverride" class ="inp_toggle no-scroll" data-current="true"></div> <div id="FaceOverride" class ="inp_toggle no-scroll" data-current="true"></div>
</div> </div>
</div> </div>
`; `;
document.getElementById('settings-implementation').appendChild(l_block); document.getElementById('settings-implementation').appendChild(l_block);
// Update sliders in new menu block // Update sliders in new menu block
let l_sliders = l_block.querySelectorAll('.inp_slider'); let l_sliders = l_block.querySelectorAll('.inp_slider');
for (var i = 0; i < l_sliders.length; i++) { for (var i = 0; i < l_sliders.length; i++) {
g_modSettingsDHT[g_modSettingsDHT.length] = new inp_slider_mod_dht(l_sliders[i], 'MelonMod_DHT_Call_InpSlider'); g_modSettingsDHT[g_modSettingsDHT.length] = new inp_slider_mod_dht(l_sliders[i], 'MelonMod_DHT_Call_InpSlider');
} }
// Update toggles in new menu block // Update toggles in new menu block
let l_toggles = l_block.querySelectorAll('.inp_toggle'); let l_toggles = l_block.querySelectorAll('.inp_toggle');
for (var i = 0; i < l_toggles.length; i++) { for (var i = 0; i < l_toggles.length; i++) {
g_modSettingsDHT[g_modSettingsDHT.length] = new inp_toggle_mod_dht(l_toggles[i], 'MelonMod_DHT_Call_InpToggle'); g_modSettingsDHT[g_modSettingsDHT.length] = new inp_toggle_mod_dht(l_toggles[i], 'MelonMod_DHT_Call_InpToggle');
} }
} }

View file

@ -1,43 +1,43 @@
using ABI_RC.Core; using ABI_RC.Core;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using UnityEngine; using UnityEngine;
namespace ml_prm namespace ml_prm
{ {
class AvatarBoolParameter class AvatarBoolParameter
{ {
public readonly string m_name; public readonly string m_name;
public readonly int m_hash = 0; public readonly int m_hash = 0;
public readonly bool m_sync; public readonly bool m_sync;
readonly CVRAnimatorManager m_manager = null; readonly CVRAnimatorManager m_manager = null;
public AvatarBoolParameter(string p_name, CVRAnimatorManager p_manager) public AvatarBoolParameter(string p_name, CVRAnimatorManager p_manager)
{ {
m_name = p_name; m_name = p_name;
m_manager = p_manager; m_manager = p_manager;
Regex l_regex = new Regex("^#?" + p_name + '$'); Regex l_regex = new Regex("^#?" + p_name + '$');
foreach(var l_param in m_manager.animator.parameters) foreach(var l_param in m_manager.animator.parameters)
{ {
if(l_regex.IsMatch(l_param.name) && (l_param.type == AnimatorControllerParameterType.Bool)) if(l_regex.IsMatch(l_param.name) && (l_param.type == AnimatorControllerParameterType.Bool))
{ {
m_name = l_param.name; m_name = l_param.name;
m_hash = l_param.nameHash; m_hash = l_param.nameHash;
m_sync = (l_param.name[0] != '#'); m_sync = (l_param.name[0] != '#');
break; break;
} }
} }
} }
public void SetValue(bool p_value) public void SetValue(bool p_value)
{ {
if(m_hash != 0) if(m_hash != 0)
{ {
if(m_sync) if(m_sync)
m_manager.SetAnimatorParameterBool(m_name, p_value); m_manager.SetAnimatorParameterBool(m_name, p_value);
else else
m_manager.animator.SetBool(m_hash, p_value); m_manager.animator.SetBool(m_hash, p_value);
} }
} }
} }
} }

View file

@ -1,216 +1,216 @@
using ABI.CCK.Components; using ABI.CCK.Components;
using ABI_RC.Core; using ABI_RC.Core;
using ABI_RC.Core.InteractionSystem; using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using ABI_RC.Core.Util.AssetFiltering; using ABI_RC.Core.Util.AssetFiltering;
using ABI_RC.Systems.IK.SubSystems; using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.MovementSystem; using ABI_RC.Systems.MovementSystem;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
namespace ml_prm namespace ml_prm
{ {
public class PlayerRagdollMod : MelonLoader.MelonMod public class PlayerRagdollMod : MelonLoader.MelonMod
{ {
static PlayerRagdollMod ms_instance = null; static PlayerRagdollMod ms_instance = null;
RagdollController m_localController = null; RagdollController m_localController = null;
public override void OnInitializeMelon() public override void OnInitializeMelon()
{ {
if(ms_instance == null) if(ms_instance == null)
ms_instance = this; ms_instance = this;
Settings.Init(); Settings.Init();
ModUi.Init(); ModUi.Init();
HarmonyInstance.Patch( HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)),
null, null,
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
); );
HarmonyInstance.Patch( HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)), typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)),
null, null,
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
); );
HarmonyInstance.Patch( HarmonyInstance.Patch(
typeof(PlayerSetup).GetMethod("SetupIKScaling", BindingFlags.NonPublic | BindingFlags.Instance), typeof(PlayerSetup).GetMethod("SetupIKScaling", BindingFlags.NonPublic | BindingFlags.Instance),
null, null,
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnSetupIKScaling_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnSetupIKScaling_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
); );
HarmonyInstance.Patch( HarmonyInstance.Patch(
typeof(CVRSeat).GetMethod(nameof(CVRSeat.SitDown)), typeof(CVRSeat).GetMethod(nameof(CVRSeat.SitDown)),
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnCVRSeatSitDown_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnCVRSeatSitDown_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
null null
); );
HarmonyInstance.Patch( HarmonyInstance.Patch(
typeof(BodySystem).GetMethod(nameof(BodySystem.StartCalibration)), typeof(BodySystem).GetMethod(nameof(BodySystem.StartCalibration)),
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnStartCalibration_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnStartCalibration_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
null null
); );
HarmonyInstance.Patch( HarmonyInstance.Patch(
typeof(RootLogic).GetMethod(nameof(RootLogic.SpawnOnWorldInstance)), typeof(RootLogic).GetMethod(nameof(RootLogic.SpawnOnWorldInstance)),
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnWorldSpawn_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnWorldSpawn_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
null null
); );
HarmonyInstance.Patch( HarmonyInstance.Patch(
typeof(CombatSystem).GetMethods().First(m => (!m.IsGenericMethod && m.Name == nameof(CombatSystem.Down))), 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)), new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnCombatDown_Prefix), BindingFlags.Static | BindingFlags.NonPublic)),
null null
); );
HarmonyInstance.Patch( HarmonyInstance.Patch(
typeof(MovementSystem).GetMethod(nameof(MovementSystem.ChangeFlight)), typeof(MovementSystem).GetMethod(nameof(MovementSystem.ChangeFlight)),
null, null,
new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnChangeFlight_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnChangeFlight_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
); );
// Whitelist the toggle script // Whitelist the toggle script
(typeof(SharedFilter).GetField("_localComponentWhitelist", BindingFlags.NonPublic | BindingFlags.Static)?.GetValue(null) as HashSet<Type>)?.Add(typeof(RagdollToggle)); (typeof(SharedFilter).GetField("_localComponentWhitelist", BindingFlags.NonPublic | BindingFlags.Static)?.GetValue(null) as HashSet<Type>)?.Add(typeof(RagdollToggle));
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer()); MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
} }
public override void OnDeinitializeMelon() public override void OnDeinitializeMelon()
{ {
if(ms_instance == this) if(ms_instance == this)
ms_instance = null; ms_instance = null;
m_localController = null; m_localController = null;
} }
System.Collections.IEnumerator WaitForLocalPlayer() System.Collections.IEnumerator WaitForLocalPlayer()
{ {
while(PlayerSetup.Instance == null) while(PlayerSetup.Instance == null)
yield return null; yield return null;
m_localController = PlayerSetup.Instance.gameObject.AddComponent<RagdollController>(); m_localController = PlayerSetup.Instance.gameObject.AddComponent<RagdollController>();
ModUi.SwitchChange += this.OnSwitchActivation; ModUi.SwitchChange += this.OnSwitchActivation;
} }
void OnSwitchActivation() void OnSwitchActivation()
{ {
if(m_localController != null) if(m_localController != null)
m_localController.SwitchRagdoll(); m_localController.SwitchRagdoll();
} }
// Patches // Patches
static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear(); static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear();
void OnAvatarClear() void OnAvatarClear()
{ {
try try
{ {
if(m_localController != null) if(m_localController != null)
m_localController.OnAvatarClear(); m_localController.OnAvatarClear();
} }
catch(Exception e) catch(Exception e)
{ {
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar(); static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar();
void OnSetupAvatar() void OnSetupAvatar()
{ {
try try
{ {
if(m_localController != null) if(m_localController != null)
m_localController.OnAvatarSetup(); m_localController.OnAvatarSetup();
} }
catch(Exception e) catch(Exception e)
{ {
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
static void OnSetupIKScaling_Postfix(ref UnityEngine.Vector3 ___scaleDifference) => ms_instance?.OnSetupIKScaling(___scaleDifference.y); static void OnSetupIKScaling_Postfix(ref UnityEngine.Vector3 ___scaleDifference) => ms_instance?.OnSetupIKScaling(___scaleDifference.y);
void OnSetupIKScaling(float p_scaleDifference) void OnSetupIKScaling(float p_scaleDifference)
{ {
try try
{ {
if(m_localController != null) if(m_localController != null)
m_localController.OnAvatarScaling(1f + p_scaleDifference); m_localController.OnAvatarScaling(1f + p_scaleDifference);
} }
catch(Exception e) catch(Exception e)
{ {
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
static void OnCVRSeatSitDown_Prefix(ref CVRSeat __instance) => ms_instance?.OnCVRSeatSitDown(__instance); static void OnCVRSeatSitDown_Prefix(ref CVRSeat __instance) => ms_instance?.OnCVRSeatSitDown(__instance);
void OnCVRSeatSitDown(CVRSeat p_seat) void OnCVRSeatSitDown(CVRSeat p_seat)
{ {
try try
{ {
if(m_localController != null) if(m_localController != null)
m_localController.OnSeatSitDown(p_seat); m_localController.OnSeatSitDown(p_seat);
} }
catch(Exception e) catch(Exception e)
{ {
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
static void OnStartCalibration_Prefix() => ms_instance?.OnStartCalibration(); static void OnStartCalibration_Prefix() => ms_instance?.OnStartCalibration();
void OnStartCalibration() void OnStartCalibration()
{ {
try try
{ {
if(m_localController != null) if(m_localController != null)
m_localController.OnStartCalibration(); m_localController.OnStartCalibration();
} }
catch(Exception e) catch(Exception e)
{ {
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
static void OnWorldSpawn_Prefix() => ms_instance?.OnWorldSpawn(); static void OnWorldSpawn_Prefix() => ms_instance?.OnWorldSpawn();
void OnWorldSpawn() void OnWorldSpawn()
{ {
try try
{ {
if(m_localController != null) if(m_localController != null)
m_localController.OnWorldSpawn(); m_localController.OnWorldSpawn();
} }
catch(Exception e) catch(Exception e)
{ {
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
static void OnCombatDown_Prefix(ref CombatSystem __instance) static void OnCombatDown_Prefix(ref CombatSystem __instance)
{ {
if((__instance == CombatSystem.Instance) && !__instance.isDown) if((__instance == CombatSystem.Instance) && !__instance.isDown)
ms_instance?.OnCombatDown(); ms_instance?.OnCombatDown();
} }
void OnCombatDown() void OnCombatDown()
{ {
try try
{ {
if(m_localController != null) if(m_localController != null)
m_localController.OnCombatDown(); m_localController.OnCombatDown();
} }
catch(Exception e) catch(Exception e)
{ {
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
static void OnChangeFlight_Postfix() => ms_instance?.OnChangeFlight(); static void OnChangeFlight_Postfix() => ms_instance?.OnChangeFlight();
void OnChangeFlight() void OnChangeFlight()
{ {
try try
{ {
if(m_localController != null) if(m_localController != null)
m_localController.OnChangeFlight(); m_localController.OnChangeFlight();
} }
catch(Exception e) catch(Exception e)
{ {
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
} }
} }

View file

@ -1,198 +1,198 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
namespace ml_prm namespace ml_prm
{ {
static class ModUi static class ModUi
{ {
enum UiIndex enum UiIndex
{ {
Hotkey = 0, Hotkey = 0,
Gravity, Gravity,
PointersReaction, PointersReaction,
IgnoreLocal, IgnoreLocal,
CombatReaction, CombatReaction,
AutoRecover, AutoRecover,
Slipperiness, Slipperiness,
Bounciness, Bounciness,
ViewVelocity, ViewVelocity,
JumpRecover, JumpRecover,
VelocityMultiplier, VelocityMultiplier,
MovementDrag, MovementDrag,
AngularDrag, AngularDrag,
RecoverDelay RecoverDelay
} }
static public event Action SwitchChange; static public event Action SwitchChange;
static List<object> ms_uiElements = null; static List<object> ms_uiElements = null;
internal static void Init() internal static void Init()
{ {
ms_uiElements = new List<object>(); ms_uiElements = new List<object>();
if(MelonLoader.MelonMod.RegisteredMelons.FirstOrDefault(m => m.Info.Name == "BTKUILib") != null) if(MelonLoader.MelonMod.RegisteredMelons.FirstOrDefault(m => m.Info.Name == "BTKUILib") != null)
CreateUi(); CreateUi();
} }
// Separated method, otherwise exception is thrown, funny CSharp and optional references, smh // Separated method, otherwise exception is thrown, funny CSharp and optional references, smh
static void CreateUi() static void CreateUi()
{ {
BTKUILib.QuickMenuAPI.PrepareIcon("PlayerRagdollMod", "PRM-Person", GetIconStream("person.png")); BTKUILib.QuickMenuAPI.PrepareIcon("PlayerRagdollMod", "PRM-Person", GetIconStream("person.png"));
var l_modRoot = new BTKUILib.UIObjects.Page("PlayerRagdollMod", "MainPage", true, "PRM-Person"); var l_modRoot = new BTKUILib.UIObjects.Page("PlayerRagdollMod", "MainPage", true, "PRM-Person");
l_modRoot.MenuTitle = "Player Ragdoll Mod"; l_modRoot.MenuTitle = "Player Ragdoll Mod";
l_modRoot.MenuSubtitle = "Become a ragdoll and change various settings for people amusement"; l_modRoot.MenuSubtitle = "Become a ragdoll and change various settings for people amusement";
var l_modCategory = l_modRoot.AddCategory("Settings"); var l_modCategory = l_modRoot.AddCategory("Settings");
l_modCategory.AddButton("Switch ragdoll", "PRM-Person", "Switch between normal and ragdoll state").OnPress += () => SwitchChange?.Invoke(); l_modCategory.AddButton("Switch ragdoll", "PRM-Person", "Switch between normal and ragdoll state").OnPress += () => SwitchChange?.Invoke();
ms_uiElements.Add(l_modCategory.AddToggle("Use hotkey", "Switch ragdoll mode with 'R' key", Settings.Hotkey)); ms_uiElements.Add(l_modCategory.AddToggle("Use hotkey", "Switch ragdoll mode with 'R' key", Settings.Hotkey));
(ms_uiElements[(int)UiIndex.Hotkey] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.Hotkey, state); (ms_uiElements[(int)UiIndex.Hotkey] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.Hotkey, state);
ms_uiElements.Add(l_modCategory.AddToggle("Use gravity", "Apply gravity to ragdoll", Settings.Gravity)); ms_uiElements.Add(l_modCategory.AddToggle("Use gravity", "Apply gravity to ragdoll", Settings.Gravity));
(ms_uiElements[(int)UiIndex.Gravity] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.Gravity, state); (ms_uiElements[(int)UiIndex.Gravity] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.Gravity, state);
ms_uiElements.Add(l_modCategory.AddToggle("Pointers reaction", "React to trigger colliders with CVRPointer component of 'ragdoll' type", Settings.PointersReaction)); ms_uiElements.Add(l_modCategory.AddToggle("Pointers reaction", "React to trigger colliders with CVRPointer component of 'ragdoll' type", Settings.PointersReaction));
(ms_uiElements[(int)UiIndex.PointersReaction] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.PointersReaction, state); (ms_uiElements[(int)UiIndex.PointersReaction] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.PointersReaction, state);
ms_uiElements.Add(l_modCategory.AddToggle("Ignore local pointers", "Ignore local avatar's CVRPointer components of 'ragdoll' type", Settings.IgnoreLocal)); ms_uiElements.Add(l_modCategory.AddToggle("Ignore local pointers", "Ignore local avatar's CVRPointer components of 'ragdoll' type", Settings.IgnoreLocal));
(ms_uiElements[(int)UiIndex.IgnoreLocal] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.IgnoreLocal, state); (ms_uiElements[(int)UiIndex.IgnoreLocal] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.IgnoreLocal, state);
ms_uiElements.Add(l_modCategory.AddToggle("Combat reaction", "Ragdoll upon combat system death", Settings.CombatReaction)); ms_uiElements.Add(l_modCategory.AddToggle("Combat reaction", "Ragdoll upon combat system death", Settings.CombatReaction));
(ms_uiElements[(int)UiIndex.CombatReaction] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.CombatReaction, state); (ms_uiElements[(int)UiIndex.CombatReaction] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.CombatReaction, state);
ms_uiElements.Add(l_modCategory.AddToggle("Auto recover", "Automatically unragdoll after set recover delay", Settings.AutoRecover)); ms_uiElements.Add(l_modCategory.AddToggle("Auto recover", "Automatically unragdoll after set recover delay", Settings.AutoRecover));
(ms_uiElements[(int)UiIndex.AutoRecover] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.AutoRecover, state); (ms_uiElements[(int)UiIndex.AutoRecover] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.AutoRecover, state);
ms_uiElements.Add(l_modCategory.AddToggle("Slipperiness", "Enables/disables friction of ragdoll", Settings.Slipperiness)); ms_uiElements.Add(l_modCategory.AddToggle("Slipperiness", "Enables/disables friction of ragdoll", Settings.Slipperiness));
(ms_uiElements[(int)UiIndex.Slipperiness] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.Slipperiness, state); (ms_uiElements[(int)UiIndex.Slipperiness] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.Slipperiness, state);
ms_uiElements.Add(l_modCategory.AddToggle("Bounciness", "Enables/disables bounciness of ragdoll", Settings.Bounciness)); ms_uiElements.Add(l_modCategory.AddToggle("Bounciness", "Enables/disables bounciness of ragdoll", Settings.Bounciness));
(ms_uiElements[(int)UiIndex.Bounciness] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.Bounciness, state); (ms_uiElements[(int)UiIndex.Bounciness] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.Bounciness, state);
ms_uiElements.Add(l_modCategory.AddToggle("View direction velocity", "Apply velocity to camera view direction", Settings.ViewVelocity)); ms_uiElements.Add(l_modCategory.AddToggle("View direction velocity", "Apply velocity to camera view direction", Settings.ViewVelocity));
(ms_uiElements[(int)UiIndex.ViewVelocity] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.ViewVelocity, state); (ms_uiElements[(int)UiIndex.ViewVelocity] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.ViewVelocity, state);
ms_uiElements.Add(l_modCategory.AddToggle("Jump recover", "Recover from ragdoll state by jumping", Settings.JumpRecover)); ms_uiElements.Add(l_modCategory.AddToggle("Jump recover", "Recover from ragdoll state by jumping", Settings.JumpRecover));
(ms_uiElements[(int)UiIndex.JumpRecover] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.JumpRecover, state); (ms_uiElements[(int)UiIndex.JumpRecover] as BTKUILib.UIObjects.Components.ToggleButton).OnValueUpdated += (state) => OnToggleUpdate(UiIndex.JumpRecover, state);
ms_uiElements.Add(l_modRoot.AddSlider("Velocity multiplier", "Velocity multiplier upon entering ragdoll state", Settings.VelocityMultiplier, 1f, 50f)); ms_uiElements.Add(l_modRoot.AddSlider("Velocity multiplier", "Velocity multiplier upon entering ragdoll state", Settings.VelocityMultiplier, 1f, 50f));
(ms_uiElements[(int)UiIndex.VelocityMultiplier] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) => OnSliderUpdate(UiIndex.VelocityMultiplier, value); (ms_uiElements[(int)UiIndex.VelocityMultiplier] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) => OnSliderUpdate(UiIndex.VelocityMultiplier, value);
ms_uiElements.Add(l_modRoot.AddSlider("Movement drag", "Movement resistance", Settings.MovementDrag, 0f, 50f)); ms_uiElements.Add(l_modRoot.AddSlider("Movement drag", "Movement resistance", Settings.MovementDrag, 0f, 50f));
(ms_uiElements[(int)UiIndex.MovementDrag] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) => OnSliderUpdate(UiIndex.MovementDrag, value); (ms_uiElements[(int)UiIndex.MovementDrag] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) => OnSliderUpdate(UiIndex.MovementDrag, value);
ms_uiElements.Add(l_modRoot.AddSlider("Angular movement drag", "Rotation movement resistance", Settings.AngularDrag, 0f, 50f)); ms_uiElements.Add(l_modRoot.AddSlider("Angular movement drag", "Rotation movement resistance", Settings.AngularDrag, 0f, 50f));
(ms_uiElements[(int)UiIndex.AngularDrag] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) => OnSliderUpdate(UiIndex.AngularDrag, value); (ms_uiElements[(int)UiIndex.AngularDrag] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) => OnSliderUpdate(UiIndex.AngularDrag, value);
ms_uiElements.Add(l_modRoot.AddSlider("Recover delay (seconds)", "Recover delay for automatic recover", Settings.RecoverDelay, 1f, 10f)); ms_uiElements.Add(l_modRoot.AddSlider("Recover delay (seconds)", "Recover delay for automatic recover", Settings.RecoverDelay, 1f, 10f));
(ms_uiElements[(int)UiIndex.RecoverDelay] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) => OnSliderUpdate(UiIndex.RecoverDelay, value); (ms_uiElements[(int)UiIndex.RecoverDelay] as BTKUILib.UIObjects.Components.SliderFloat).OnValueUpdated += (value) => OnSliderUpdate(UiIndex.RecoverDelay, value);
l_modCategory.AddButton("Reset settings", "", "Reset mod settings to default").OnPress += Reset; l_modCategory.AddButton("Reset settings", "", "Reset mod settings to default").OnPress += Reset;
} }
static void OnToggleUpdate(UiIndex p_index, bool p_state, bool p_force = false) static void OnToggleUpdate(UiIndex p_index, bool p_state, bool p_force = false)
{ {
switch(p_index) switch(p_index)
{ {
case UiIndex.Hotkey: case UiIndex.Hotkey:
Settings.SetSetting(Settings.ModSetting.Hotkey, p_state); Settings.SetSetting(Settings.ModSetting.Hotkey, p_state);
break; break;
case UiIndex.Gravity: case UiIndex.Gravity:
Settings.SetSetting(Settings.ModSetting.Gravity, p_state); Settings.SetSetting(Settings.ModSetting.Gravity, p_state);
break; break;
case UiIndex.PointersReaction: case UiIndex.PointersReaction:
Settings.SetSetting(Settings.ModSetting.PointersReaction, p_state); Settings.SetSetting(Settings.ModSetting.PointersReaction, p_state);
break; break;
case UiIndex.IgnoreLocal: case UiIndex.IgnoreLocal:
Settings.SetSetting(Settings.ModSetting.IgnoreLocal, p_state); Settings.SetSetting(Settings.ModSetting.IgnoreLocal, p_state);
break; break;
case UiIndex.CombatReaction: case UiIndex.CombatReaction:
Settings.SetSetting(Settings.ModSetting.CombatReaction, p_state); Settings.SetSetting(Settings.ModSetting.CombatReaction, p_state);
break; break;
case UiIndex.AutoRecover: case UiIndex.AutoRecover:
Settings.SetSetting(Settings.ModSetting.AutoRecover, p_state); Settings.SetSetting(Settings.ModSetting.AutoRecover, p_state);
break; break;
case UiIndex.Slipperiness: case UiIndex.Slipperiness:
Settings.SetSetting(Settings.ModSetting.Slipperiness, p_state); Settings.SetSetting(Settings.ModSetting.Slipperiness, p_state);
break; break;
case UiIndex.Bounciness: case UiIndex.Bounciness:
Settings.SetSetting(Settings.ModSetting.Bounciness, p_state); Settings.SetSetting(Settings.ModSetting.Bounciness, p_state);
break; break;
case UiIndex.ViewVelocity: case UiIndex.ViewVelocity:
Settings.SetSetting(Settings.ModSetting.ViewVelocity, p_state); Settings.SetSetting(Settings.ModSetting.ViewVelocity, p_state);
break; break;
case UiIndex.JumpRecover: case UiIndex.JumpRecover:
Settings.SetSetting(Settings.ModSetting.JumpRecover, p_state); Settings.SetSetting(Settings.ModSetting.JumpRecover, p_state);
break; break;
} }
if(p_force) if(p_force)
(ms_uiElements[(int)p_index] as BTKUILib.UIObjects.Components.ToggleButton).ToggleValue = p_state; (ms_uiElements[(int)p_index] as BTKUILib.UIObjects.Components.ToggleButton).ToggleValue = p_state;
} }
static void OnSliderUpdate(UiIndex p_index, float p_value, bool p_force = false) static void OnSliderUpdate(UiIndex p_index, float p_value, bool p_force = false)
{ {
switch(p_index) switch(p_index)
{ {
case UiIndex.VelocityMultiplier: case UiIndex.VelocityMultiplier:
Settings.SetSetting(Settings.ModSetting.VelocityMultiplier, p_value); Settings.SetSetting(Settings.ModSetting.VelocityMultiplier, p_value);
break; break;
case UiIndex.MovementDrag: case UiIndex.MovementDrag:
Settings.SetSetting(Settings.ModSetting.MovementDrag, p_value); Settings.SetSetting(Settings.ModSetting.MovementDrag, p_value);
break; break;
case UiIndex.AngularDrag: case UiIndex.AngularDrag:
Settings.SetSetting(Settings.ModSetting.AngularDrag, p_value); Settings.SetSetting(Settings.ModSetting.AngularDrag, p_value);
break; break;
case UiIndex.RecoverDelay: case UiIndex.RecoverDelay:
Settings.SetSetting(Settings.ModSetting.RecoverDelay, p_value); Settings.SetSetting(Settings.ModSetting.RecoverDelay, p_value);
break; break;
} }
if(p_force) if(p_force)
(ms_uiElements[(int)p_index] as BTKUILib.UIObjects.Components.SliderFloat).SetSliderValue(p_value); (ms_uiElements[(int)p_index] as BTKUILib.UIObjects.Components.SliderFloat).SetSliderValue(p_value);
} }
static void Reset() static void Reset()
{ {
OnToggleUpdate(UiIndex.Hotkey, true, true); OnToggleUpdate(UiIndex.Hotkey, true, true);
OnToggleUpdate(UiIndex.Gravity, true, true); OnToggleUpdate(UiIndex.Gravity, true, true);
OnToggleUpdate(UiIndex.PointersReaction, true, true); OnToggleUpdate(UiIndex.PointersReaction, true, true);
OnToggleUpdate(UiIndex.IgnoreLocal, true, true); OnToggleUpdate(UiIndex.IgnoreLocal, true, true);
OnToggleUpdate(UiIndex.CombatReaction, true, true); OnToggleUpdate(UiIndex.CombatReaction, true, true);
OnToggleUpdate(UiIndex.AutoRecover, false, true); OnToggleUpdate(UiIndex.AutoRecover, false, true);
OnToggleUpdate(UiIndex.Slipperiness, false, true); OnToggleUpdate(UiIndex.Slipperiness, false, true);
OnToggleUpdate(UiIndex.Bounciness, false, true); OnToggleUpdate(UiIndex.Bounciness, false, true);
OnToggleUpdate(UiIndex.ViewVelocity, false, true); OnToggleUpdate(UiIndex.ViewVelocity, false, true);
OnToggleUpdate(UiIndex.JumpRecover, false, true); OnToggleUpdate(UiIndex.JumpRecover, false, true);
OnSliderUpdate(UiIndex.VelocityMultiplier, 2f, true); OnSliderUpdate(UiIndex.VelocityMultiplier, 2f, true);
OnSliderUpdate(UiIndex.MovementDrag, 2f, true); OnSliderUpdate(UiIndex.MovementDrag, 2f, true);
OnSliderUpdate(UiIndex.AngularDrag, 2f, true); OnSliderUpdate(UiIndex.AngularDrag, 2f, true);
OnSliderUpdate(UiIndex.RecoverDelay, 3f, true); OnSliderUpdate(UiIndex.RecoverDelay, 3f, true);
} }
static Stream GetIconStream(string p_name) static Stream GetIconStream(string p_name)
{ {
Assembly l_assembly = Assembly.GetExecutingAssembly(); Assembly l_assembly = Assembly.GetExecutingAssembly();
string l_assemblyName = l_assembly.GetName().Name; string l_assemblyName = l_assembly.GetName().Name;
return l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name); return l_assembly.GetManifestResourceStream(l_assemblyName + ".resources." + p_name);
} }
} }
} }

View file

@ -1,12 +1,12 @@
using UnityEngine; using UnityEngine;
namespace ml_prm namespace ml_prm
{ {
public class RagdollToggle : MonoBehaviour public class RagdollToggle : MonoBehaviour
{ {
[Tooltip("Whether or not is should use the isOn property to override the current Ragdoll State of the Avatar.")] [Tooltip("Whether or not is should use the isOn property to override the current Ragdoll State of the Avatar.")]
[SerializeField] public bool shouldOverride; [SerializeField] public bool shouldOverride;
[Tooltip("Whether Ragdoll State is active or not on the Avatar. Requires shouldOverride to be true to work.")] [Tooltip("Whether Ragdoll State is active or not on the Avatar. Requires shouldOverride to be true to work.")]
[SerializeField] public bool isOn; [SerializeField] public bool isOn;
} }
} }

View file

@ -1,223 +1,223 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
namespace ml_prm namespace ml_prm
{ {
static class Settings static class Settings
{ {
public enum ModSetting public enum ModSetting
{ {
Hotkey = 0, Hotkey = 0,
HotkeyKey, HotkeyKey,
VelocityMultiplier, VelocityMultiplier,
MovementDrag, MovementDrag,
AngularDrag, AngularDrag,
Gravity, Gravity,
PointersReaction, PointersReaction,
IgnoreLocal, IgnoreLocal,
CombatReaction, CombatReaction,
AutoRecover, AutoRecover,
RecoverDelay, RecoverDelay,
Slipperiness, Slipperiness,
Bounciness, Bounciness,
ViewVelocity, ViewVelocity,
JumpRecover JumpRecover
} }
public static bool Hotkey { get; private set; } = true; public static bool Hotkey { get; private set; } = true;
public static KeyCode HotkeyKey { get; private set; } = KeyCode.R; public static KeyCode HotkeyKey { get; private set; } = KeyCode.R;
public static float VelocityMultiplier { get; private set; } = 2f; public static float VelocityMultiplier { get; private set; } = 2f;
public static float MovementDrag { get; private set; } = 2f; public static float MovementDrag { get; private set; } = 2f;
public static float AngularDrag { get; private set; } = 2f; public static float AngularDrag { get; private set; } = 2f;
public static bool Gravity { get; private set; } = true; public static bool Gravity { get; private set; } = true;
public static bool PointersReaction { get; private set; } = true; public static bool PointersReaction { get; private set; } = true;
public static bool IgnoreLocal { get; private set; } = true; public static bool IgnoreLocal { get; private set; } = true;
public static bool CombatReaction { get; private set; } = true; public static bool CombatReaction { get; private set; } = true;
public static bool AutoRecover { get; private set; } = false; public static bool AutoRecover { get; private set; } = false;
public static float RecoverDelay { get; private set; } = 3f; public static float RecoverDelay { get; private set; } = 3f;
public static bool Slipperiness { get; private set; } = false; public static bool Slipperiness { get; private set; } = false;
public static bool Bounciness { get; private set; } = false; public static bool Bounciness { get; private set; } = false;
public static bool ViewVelocity { get; private set; } = false; public static bool ViewVelocity { get; private set; } = false;
public static bool JumpRecover { get; private set; } = false; public static bool JumpRecover { get; private set; } = false;
static public event Action<bool> HotkeyChange; static public event Action<bool> HotkeyChange;
static public event Action<KeyCode> HotkeyKeyChange; static public event Action<KeyCode> HotkeyKeyChange;
static public event Action<float> VelocityMultiplierChange; static public event Action<float> VelocityMultiplierChange;
static public event Action<float> MovementDragChange; static public event Action<float> MovementDragChange;
static public event Action<float> AngularDragChange; static public event Action<float> AngularDragChange;
static public event Action<bool> GravityChange; static public event Action<bool> GravityChange;
static public event Action<bool> PointersReactionChange; static public event Action<bool> PointersReactionChange;
static public event Action<bool> IgnoreLocalChange; static public event Action<bool> IgnoreLocalChange;
static public event Action<bool> CombatReactionChange; static public event Action<bool> CombatReactionChange;
static public event Action<bool> AutoRecoverChange; static public event Action<bool> AutoRecoverChange;
static public event Action<float> RecoverDelayChange; static public event Action<float> RecoverDelayChange;
static public event Action<bool> SlipperinessChange; static public event Action<bool> SlipperinessChange;
static public event Action<bool> BouncinessChange; static public event Action<bool> BouncinessChange;
static public event Action<bool> ViewVelocityChange; static public event Action<bool> ViewVelocityChange;
static public event Action<bool> JumpRecoverChange; static public event Action<bool> JumpRecoverChange;
static MelonLoader.MelonPreferences_Category ms_category = null; static MelonLoader.MelonPreferences_Category ms_category = null;
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null; static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
internal static void Init() internal static void Init()
{ {
ms_category = MelonLoader.MelonPreferences.CreateCategory("PRM", "Player Ragdoll Mod"); ms_category = MelonLoader.MelonPreferences.CreateCategory("PRM", "Player Ragdoll Mod");
ms_entries = new List<MelonLoader.MelonPreferences_Entry>() ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
{ {
ms_category.CreateEntry(ModSetting.Hotkey.ToString(), Hotkey, null, null, true), ms_category.CreateEntry(ModSetting.Hotkey.ToString(), Hotkey, null, null, true),
ms_category.CreateEntry(ModSetting.HotkeyKey.ToString(), HotkeyKey, "Hotkey"), ms_category.CreateEntry(ModSetting.HotkeyKey.ToString(), HotkeyKey, "Hotkey"),
ms_category.CreateEntry(ModSetting.VelocityMultiplier.ToString(), VelocityMultiplier, null, null, true), ms_category.CreateEntry(ModSetting.VelocityMultiplier.ToString(), VelocityMultiplier, null, null, true),
ms_category.CreateEntry(ModSetting.MovementDrag.ToString(), MovementDrag, null, null, true), ms_category.CreateEntry(ModSetting.MovementDrag.ToString(), MovementDrag, null, null, true),
ms_category.CreateEntry(ModSetting.AngularDrag.ToString(), AngularDrag, null, null, true), ms_category.CreateEntry(ModSetting.AngularDrag.ToString(), AngularDrag, null, null, true),
ms_category.CreateEntry(ModSetting.Gravity.ToString(), Gravity, null, null, true), ms_category.CreateEntry(ModSetting.Gravity.ToString(), Gravity, null, null, true),
ms_category.CreateEntry(ModSetting.PointersReaction.ToString(), PointersReaction, null, null, true), ms_category.CreateEntry(ModSetting.PointersReaction.ToString(), PointersReaction, null, null, true),
ms_category.CreateEntry(ModSetting.IgnoreLocal.ToString(), IgnoreLocal, null, null, true), ms_category.CreateEntry(ModSetting.IgnoreLocal.ToString(), IgnoreLocal, null, null, true),
ms_category.CreateEntry(ModSetting.CombatReaction.ToString(), CombatReaction, null, null, true), ms_category.CreateEntry(ModSetting.CombatReaction.ToString(), CombatReaction, null, null, true),
ms_category.CreateEntry(ModSetting.AutoRecover.ToString(), AutoRecover, null, null, true), ms_category.CreateEntry(ModSetting.AutoRecover.ToString(), AutoRecover, null, null, true),
ms_category.CreateEntry(ModSetting.RecoverDelay.ToString(), RecoverDelay, null, null, true), ms_category.CreateEntry(ModSetting.RecoverDelay.ToString(), RecoverDelay, null, null, true),
ms_category.CreateEntry(ModSetting.Slipperiness.ToString(), Slipperiness, null, null, true), ms_category.CreateEntry(ModSetting.Slipperiness.ToString(), Slipperiness, null, null, true),
ms_category.CreateEntry(ModSetting.Bounciness.ToString(), Bounciness, null, null, true), ms_category.CreateEntry(ModSetting.Bounciness.ToString(), Bounciness, null, null, true),
ms_category.CreateEntry(ModSetting.ViewVelocity.ToString(), ViewVelocity, null, null, true), ms_category.CreateEntry(ModSetting.ViewVelocity.ToString(), ViewVelocity, null, null, true),
ms_category.CreateEntry(ModSetting.JumpRecover.ToString(), JumpRecover, null, null, true) ms_category.CreateEntry(ModSetting.JumpRecover.ToString(), JumpRecover, null, null, true)
}; };
ms_entries[(int)ModSetting.HotkeyKey].OnEntryValueChangedUntyped.Subscribe(OnMelonSettingSave_HotkeyKey); ms_entries[(int)ModSetting.HotkeyKey].OnEntryValueChangedUntyped.Subscribe(OnMelonSettingSave_HotkeyKey);
Hotkey = (bool)ms_entries[(int)ModSetting.Hotkey].BoxedValue; Hotkey = (bool)ms_entries[(int)ModSetting.Hotkey].BoxedValue;
HotkeyKey = (KeyCode)ms_entries[(int)ModSetting.HotkeyKey].BoxedValue; HotkeyKey = (KeyCode)ms_entries[(int)ModSetting.HotkeyKey].BoxedValue;
VelocityMultiplier = Mathf.Clamp((float)ms_entries[(int)ModSetting.VelocityMultiplier].BoxedValue, 1f, 50f); VelocityMultiplier = Mathf.Clamp((float)ms_entries[(int)ModSetting.VelocityMultiplier].BoxedValue, 1f, 50f);
MovementDrag = Mathf.Clamp((float)ms_entries[(int)ModSetting.MovementDrag].BoxedValue, 0f, 50f); MovementDrag = Mathf.Clamp((float)ms_entries[(int)ModSetting.MovementDrag].BoxedValue, 0f, 50f);
AngularDrag = Mathf.Clamp((float)ms_entries[(int)ModSetting.AngularDrag].BoxedValue, 0f, 50f); AngularDrag = Mathf.Clamp((float)ms_entries[(int)ModSetting.AngularDrag].BoxedValue, 0f, 50f);
Gravity = (bool)ms_entries[(int)ModSetting.Gravity].BoxedValue; Gravity = (bool)ms_entries[(int)ModSetting.Gravity].BoxedValue;
PointersReaction = (bool)ms_entries[(int)ModSetting.PointersReaction].BoxedValue; PointersReaction = (bool)ms_entries[(int)ModSetting.PointersReaction].BoxedValue;
IgnoreLocal = (bool)ms_entries[(int)ModSetting.IgnoreLocal].BoxedValue; IgnoreLocal = (bool)ms_entries[(int)ModSetting.IgnoreLocal].BoxedValue;
CombatReaction = (bool)ms_entries[(int)ModSetting.CombatReaction].BoxedValue; CombatReaction = (bool)ms_entries[(int)ModSetting.CombatReaction].BoxedValue;
AutoRecover = (bool)ms_entries[(int)ModSetting.AutoRecover].BoxedValue; AutoRecover = (bool)ms_entries[(int)ModSetting.AutoRecover].BoxedValue;
RecoverDelay = Mathf.Clamp((float)ms_entries[(int)ModSetting.RecoverDelay].BoxedValue, 1f, 10f); RecoverDelay = Mathf.Clamp((float)ms_entries[(int)ModSetting.RecoverDelay].BoxedValue, 1f, 10f);
Slipperiness = (bool)ms_entries[(int)ModSetting.Slipperiness].BoxedValue; Slipperiness = (bool)ms_entries[(int)ModSetting.Slipperiness].BoxedValue;
Bounciness = (bool)ms_entries[(int)ModSetting.Bounciness].BoxedValue; Bounciness = (bool)ms_entries[(int)ModSetting.Bounciness].BoxedValue;
ViewVelocity = (bool)ms_entries[(int)ModSetting.ViewVelocity].BoxedValue; ViewVelocity = (bool)ms_entries[(int)ModSetting.ViewVelocity].BoxedValue;
JumpRecover = (bool)ms_entries[(int)ModSetting.JumpRecover].BoxedValue; JumpRecover = (bool)ms_entries[(int)ModSetting.JumpRecover].BoxedValue;
} }
static void OnMelonSettingSave_HotkeyKey(object p_oldValue, object p_newValue) static void OnMelonSettingSave_HotkeyKey(object p_oldValue, object p_newValue)
{ {
if(p_newValue is KeyCode) if(p_newValue is KeyCode)
{ {
HotkeyKey = (KeyCode)p_newValue; HotkeyKey = (KeyCode)p_newValue;
HotkeyKeyChange?.Invoke(HotkeyKey); HotkeyKeyChange?.Invoke(HotkeyKey);
} }
} }
public static void SetSetting(ModSetting p_settings, object p_value) public static void SetSetting(ModSetting p_settings, object p_value)
{ {
switch(p_settings) switch(p_settings)
{ {
// Booleans // Booleans
case ModSetting.Hotkey: case ModSetting.Hotkey:
{ {
Hotkey = (bool)p_value; Hotkey = (bool)p_value;
HotkeyChange?.Invoke((bool)p_value); HotkeyChange?.Invoke((bool)p_value);
} }
break; break;
case ModSetting.Gravity: case ModSetting.Gravity:
{ {
Gravity = (bool)p_value; Gravity = (bool)p_value;
GravityChange?.Invoke((bool)p_value); GravityChange?.Invoke((bool)p_value);
} }
break; break;
case ModSetting.PointersReaction: case ModSetting.PointersReaction:
{ {
PointersReaction = (bool)p_value; PointersReaction = (bool)p_value;
PointersReactionChange?.Invoke((bool)p_value); PointersReactionChange?.Invoke((bool)p_value);
} }
break; break;
case ModSetting.IgnoreLocal: case ModSetting.IgnoreLocal:
{ {
IgnoreLocal = (bool)p_value; IgnoreLocal = (bool)p_value;
IgnoreLocalChange?.Invoke((bool)p_value); IgnoreLocalChange?.Invoke((bool)p_value);
} }
break; break;
case ModSetting.CombatReaction: case ModSetting.CombatReaction:
{ {
CombatReaction = (bool)p_value; CombatReaction = (bool)p_value;
CombatReactionChange?.Invoke((bool)p_value); CombatReactionChange?.Invoke((bool)p_value);
} }
break; break;
case ModSetting.AutoRecover: case ModSetting.AutoRecover:
{ {
AutoRecover = (bool)p_value; AutoRecover = (bool)p_value;
AutoRecoverChange?.Invoke((bool)p_value); AutoRecoverChange?.Invoke((bool)p_value);
} }
break; break;
case ModSetting.Slipperiness: case ModSetting.Slipperiness:
{ {
Slipperiness = (bool)p_value; Slipperiness = (bool)p_value;
SlipperinessChange?.Invoke((bool)p_value); SlipperinessChange?.Invoke((bool)p_value);
} }
break; break;
case ModSetting.Bounciness: case ModSetting.Bounciness:
{ {
Bounciness = (bool)p_value; Bounciness = (bool)p_value;
BouncinessChange?.Invoke((bool)p_value); BouncinessChange?.Invoke((bool)p_value);
} }
break; break;
case ModSetting.ViewVelocity: case ModSetting.ViewVelocity:
{ {
ViewVelocity = (bool)p_value; ViewVelocity = (bool)p_value;
ViewVelocityChange?.Invoke((bool)p_value); ViewVelocityChange?.Invoke((bool)p_value);
} }
break; break;
case ModSetting.JumpRecover: case ModSetting.JumpRecover:
{ {
JumpRecover = (bool)p_value; JumpRecover = (bool)p_value;
JumpRecoverChange?.Invoke((bool)p_value); JumpRecoverChange?.Invoke((bool)p_value);
} }
break; break;
// Floats // Floats
case ModSetting.VelocityMultiplier: case ModSetting.VelocityMultiplier:
{ {
VelocityMultiplier = (float)p_value; VelocityMultiplier = (float)p_value;
VelocityMultiplierChange?.Invoke((float)p_value); VelocityMultiplierChange?.Invoke((float)p_value);
} }
break; break;
case ModSetting.MovementDrag: case ModSetting.MovementDrag:
{ {
MovementDrag = (float)p_value; MovementDrag = (float)p_value;
MovementDragChange?.Invoke((float)p_value); MovementDragChange?.Invoke((float)p_value);
} }
break; break;
case ModSetting.AngularDrag: case ModSetting.AngularDrag:
{ {
AngularDrag = (float)p_value; AngularDrag = (float)p_value;
AngularDragChange?.Invoke((float)p_value); AngularDragChange?.Invoke((float)p_value);
} }
break; break;
case ModSetting.RecoverDelay: case ModSetting.RecoverDelay:
{ {
RecoverDelay = (float)p_value; RecoverDelay = (float)p_value;
RecoverDelayChange?.Invoke((float)p_value); RecoverDelayChange?.Invoke((float)p_value);
} }
break; break;
} }
if(ms_entries != null) if(ms_entries != null)
ms_entries[(int)p_settings].BoxedValue = p_value; ms_entries[(int)p_settings].BoxedValue = p_value;
} }
} }
} }

View file

@ -1,39 +1,39 @@
using ABI.CCK.Components; using ABI.CCK.Components;
using ABI_RC.Core.Savior; using ABI_RC.Core.Savior;
using ABI_RC.Systems.MovementSystem; using ABI_RC.Systems.MovementSystem;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
namespace ml_prm namespace ml_prm
{ {
static class Utils static class Utils
{ {
static readonly FieldInfo ms_groundedRaw = typeof(MovementSystem).GetField("_isGroundedRaw", BindingFlags.NonPublic | BindingFlags.Instance); static readonly FieldInfo ms_groundedRaw = typeof(MovementSystem).GetField("_isGroundedRaw", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly FieldInfo ms_appliedGravity = typeof(MovementSystem).GetField("_appliedGravity", BindingFlags.NonPublic | BindingFlags.Instance); static readonly FieldInfo ms_appliedGravity = typeof(MovementSystem).GetField("_appliedGravity", BindingFlags.NonPublic | BindingFlags.Instance);
public static bool IsInVR() => ((CheckVR.Instance != null) && CheckVR.Instance.hasVrDeviceLoaded); public static bool IsInVR() => ((CheckVR.Instance != null) && CheckVR.Instance.hasVrDeviceLoaded);
public static bool IsWorldSafe() => ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying); public static bool IsWorldSafe() => ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying);
public static float GetWorldMovementLimit() public static float GetWorldMovementLimit()
{ {
float l_result = 1f; float l_result = 1f;
if(CVRWorld.Instance != null) if(CVRWorld.Instance != null)
{ {
l_result = CVRWorld.Instance.baseMovementSpeed; l_result = CVRWorld.Instance.baseMovementSpeed;
l_result *= CVRWorld.Instance.sprintMultiplier; l_result *= CVRWorld.Instance.sprintMultiplier;
l_result *= CVRWorld.Instance.inAirMovementMultiplier; l_result *= CVRWorld.Instance.inAirMovementMultiplier;
l_result *= CVRWorld.Instance.flyMultiplier; l_result *= CVRWorld.Instance.flyMultiplier;
} }
return l_result; return l_result;
} }
public static bool IsGrounded(this MovementSystem p_instance) => (bool)ms_groundedRaw.GetValue(p_instance); public static bool IsGrounded(this MovementSystem p_instance) => (bool)ms_groundedRaw.GetValue(p_instance);
public static Vector3 GetAppliedGravity(this MovementSystem p_instance) => (Vector3)ms_appliedGravity.GetValue(p_instance); public static Vector3 GetAppliedGravity(this MovementSystem p_instance) => (Vector3)ms_appliedGravity.GetValue(p_instance);
public static void SetAppliedGravity(this MovementSystem p_instance, Vector3 p_vec) => ms_appliedGravity.SetValue(p_instance, p_vec); public static void SetAppliedGravity(this MovementSystem p_instance, Vector3 p_vec) => ms_appliedGravity.SetValue(p_instance, p_vec);
public static void CopyGlobal(this Transform p_source, Transform p_target) public static void CopyGlobal(this Transform p_source, Transform p_target)
{ {
p_target.position = p_source.position; p_target.position = p_source.position;
p_target.rotation = p_source.rotation; p_target.rotation = p_source.rotation;
} }
} }
} }

View file

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Before After
Before After

View file

@ -1,120 +1,118 @@
using ABI.CCK.Components; using ABI_RC.Core.Player;
using ABI_RC.Core.Player; using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.IK.SubSystems; using System;
using System; using System.Collections;
using System.Collections; using System.Reflection;
using System.Reflection;
namespace ml_amt
namespace ml_amt {
{ public class AvatarMotionTweaker : MelonLoader.MelonMod
public class AvatarMotionTweaker : MelonLoader.MelonMod {
{ static AvatarMotionTweaker ms_instance = null;
static AvatarMotionTweaker ms_instance = null;
MotionTweaker m_localTweaker = null;
MotionTweaker m_localTweaker = null;
public override void OnInitializeMelon()
public override void OnInitializeMelon() {
{ if(ms_instance == null)
if(ms_instance == null) ms_instance = this;
ms_instance = this;
Settings.Init();
Settings.Init();
HarmonyInstance.Patch(
HarmonyInstance.Patch( typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)),
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), null,
null, new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static))
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnAvatarClear_Postfix), BindingFlags.NonPublic | BindingFlags.Static)) );
); HarmonyInstance.Patch(
HarmonyInstance.Patch( typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)),
typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)), null,
null, new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) );
); HarmonyInstance.Patch(
HarmonyInstance.Patch( typeof(BodySystem).GetMethod(nameof(BodySystem.Calibrate)),
typeof(BodySystem).GetMethod(nameof(BodySystem.Calibrate)), null,
null, new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnCalibrate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnCalibrate_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) );
); HarmonyInstance.Patch(
HarmonyInstance.Patch( typeof(PlayerSetup).GetMethod("SetPlaySpaceScale", BindingFlags.NonPublic | BindingFlags.Instance),
typeof(PlayerSetup).GetMethod("SetPlaySpaceScale", BindingFlags.NonPublic | BindingFlags.Instance), null,
null, new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnPlayspaceScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnPlayspaceScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) );
);
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
ModSupporter.Init(); }
MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer());
} IEnumerator WaitForLocalPlayer()
{
IEnumerator WaitForLocalPlayer() while(PlayerSetup.Instance == null)
{ yield return null;
while(PlayerSetup.Instance == null)
yield return null; m_localTweaker = PlayerSetup.Instance.gameObject.AddComponent<MotionTweaker>();
}
m_localTweaker = PlayerSetup.Instance.gameObject.AddComponent<MotionTweaker>();
} public override void OnDeinitializeMelon()
{
public override void OnDeinitializeMelon() if(ms_instance == this)
{ ms_instance = null;
if(ms_instance == this)
ms_instance = null; m_localTweaker = null;
}
m_localTweaker = null;
} static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear();
void OnAvatarClear()
static void OnAvatarClear_Postfix() => ms_instance?.OnAvatarClear(); {
void OnAvatarClear() try
{ {
try if(m_localTweaker != null)
{ m_localTweaker.OnAvatarClear();
if(m_localTweaker != null) }
m_localTweaker.OnAvatarClear(); catch(Exception l_exception)
} {
catch(Exception l_exception) MelonLoader.MelonLogger.Error(l_exception);
{ }
MelonLoader.MelonLogger.Error(l_exception); }
}
} static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar();
void OnSetupAvatar()
static void OnSetupAvatar_Postfix() => ms_instance?.OnSetupAvatar(); {
void OnSetupAvatar() try
{ {
try if(m_localTweaker != null)
{ m_localTweaker.OnSetupAvatar();
if(m_localTweaker != null) }
m_localTweaker.OnSetupAvatar(); catch(Exception l_exception)
} {
catch(Exception l_exception) MelonLoader.MelonLogger.Error(l_exception);
{ }
MelonLoader.MelonLogger.Error(l_exception); }
}
} static void OnCalibrate_Postfix() => ms_instance?.OnCalibrate();
void OnCalibrate()
static void OnCalibrate_Postfix() => ms_instance?.OnCalibrate(); {
void OnCalibrate() try
{ {
try if(m_localTweaker != null)
{ m_localTweaker.OnCalibrate();
if(m_localTweaker != null) }
m_localTweaker.OnCalibrate(); catch(Exception l_exception)
} {
catch(Exception l_exception) MelonLoader.MelonLogger.Error(l_exception);
{ }
MelonLoader.MelonLogger.Error(l_exception); }
}
} static void OnPlayspaceScale_Postfix() => ms_instance?.OnPlayspaceScale();
void OnPlayspaceScale()
static void OnPlayspaceScale_Postfix() => ms_instance?.OnPlayspaceScale(); {
void OnPlayspaceScale() try
{ {
try if(m_localTweaker != null)
{ m_localTweaker.OnPlayspaceScale();
if(m_localTweaker != null) }
m_localTweaker.OnPlayspaceScale(); catch(Exception l_exception)
} {
catch(Exception l_exception) MelonLoader.MelonLogger.Error(l_exception);
{ }
MelonLoader.MelonLogger.Error(l_exception); }
} }
} }
}
}

View file

@ -1,49 +0,0 @@
using System.Collections;
using System.Linq;
namespace ml_amt
{
static class ModSupporter
{
static bool ms_ragdollMod = false;
static bool ms_copycatMod = false;
public static void Init()
{
if(MelonLoader.MelonMod.RegisteredMelons.FirstOrDefault(m => m.Info.Name == "PlayerRagdollMod") != null)
MelonLoader.MelonCoroutines.Start(WaitForRagdollInstance());
if(MelonLoader.MelonMod.RegisteredMelons.FirstOrDefault(m => m.Info.Name == "PlayerMovementCopycat") != null)
MelonLoader.MelonCoroutines.Start(WaitForCopycatInstance());
}
// PlayerRagdollMod support
static IEnumerator WaitForRagdollInstance()
{
while(ml_prm.RagdollController.Instance == null)
yield return null;
ms_ragdollMod = true;
}
static bool IsRagdolled() => ml_prm.RagdollController.Instance.IsRagdolled();
// 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();
public static bool SkipHipsOverride()
{
bool l_result = false;
l_result |= (ms_ragdollMod && IsRagdolled());
l_result |= (ms_copycatMod && IsCopycating());
return l_result;
}
}
}

View file

@ -1,345 +1,316 @@
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using ABI_RC.Systems.IK; using ABI_RC.Systems.IK;
using ABI_RC.Systems.IK.SubSystems; using ABI_RC.Systems.IK.SubSystems;
using ABI_RC.Systems.MovementSystem; using ABI_RC.Systems.MovementSystem;
using RootMotion.FinalIK; using RootMotion.FinalIK;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
namespace ml_amt namespace ml_amt
{ {
[DisallowMultipleComponent] [DisallowMultipleComponent]
class MotionTweaker : MonoBehaviour class MotionTweaker : MonoBehaviour
{ {
struct IKState struct IKState
{ {
public float m_weight; public float m_weight;
public float m_locomotionWeight; public float m_locomotionWeight;
public bool m_plantFeet; public bool m_plantFeet;
public bool m_bendNormalLeft; public bool m_bendNormalLeft;
public bool m_bendNormalRight; public bool m_bendNormalRight;
} }
static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f); static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f);
static readonly int ms_emoteHash = Animator.StringToHash("Emote"); static readonly int ms_emoteHash = Animator.StringToHash("Emote");
IKState m_ikState; IKState m_ikState;
VRIK m_vrIk = null; VRIK m_vrIk = null;
int m_locomotionLayer = 0; int m_locomotionLayer = 0;
float m_avatarScale = 1f; float m_avatarScale = 1f;
Vector3 m_locomotionOffset = Vector3.zero; // Original locomotion offset Vector3 m_locomotionOffset = Vector3.zero; // Original locomotion offset
Transform m_avatarHips = null; bool m_inVR = false;
bool m_inVR = false;
bool m_avatarReady = false;
bool m_avatarReady = false; bool m_grounded = false;
bool m_grounded = false; bool m_groundedRaw = false;
bool m_groundedRaw = false; bool m_moving = false;
bool m_moving = false; bool m_locomotionOverride = false;
bool m_locomotionOverride = false;
bool m_ikOverrideFly = true;
bool m_ikOverrideFly = true; bool m_ikOverrideJump = true;
bool m_ikOverrideJump = true;
bool m_detectEmotes = true;
bool m_detectEmotes = true; bool m_emoteActive = false;
bool m_emoteActive = false;
Vector3 m_massCenter = Vector3.zero;
bool m_followHips = true;
Vector3 m_hipsToPlayer = Vector3.zero; Transform m_ikLimits = null;
Vector3 m_massCenter = Vector3.zero; readonly List<AvatarParameter> m_parameters = null;
Transform m_ikLimits = null; internal MotionTweaker()
{
readonly List<AvatarParameter> m_parameters = null; m_parameters = new List<AvatarParameter>();
}
internal MotionTweaker()
{ // Unity events
m_parameters = new List<AvatarParameter>(); void Start()
} {
m_inVR = Utils.IsInVR();
// Unity events
void Start() SetCrouchLimit(Settings.CrouchLimit);
{ SetProneLimit(Settings.ProneLimit);
m_inVR = Utils.IsInVR(); SetIKOverrideFly(Settings.IKOverrideFly);
SetIKOverrideJump(Settings.IKOverrideJump);
SetCrouchLimit(Settings.CrouchLimit); SetDetectEmotes(Settings.DetectEmotes);
SetProneLimit(Settings.ProneLimit);
SetIKOverrideFly(Settings.IKOverrideFly); Settings.CrouchLimitChange += this.SetCrouchLimit;
SetIKOverrideJump(Settings.IKOverrideJump); Settings.ProneLimitChange += this.SetProneLimit;
SetDetectEmotes(Settings.DetectEmotes); Settings.IKOverrideFlyChange += this.SetIKOverrideFly;
SetFollowHips(Settings.FollowHips); Settings.IKOverrideJumpChange += this.SetIKOverrideJump;
Settings.DetectEmotesChange += this.SetDetectEmotes;
Settings.CrouchLimitChange += this.SetCrouchLimit; Settings.MassCenterChange += this.OnMassCenterChange;
Settings.ProneLimitChange += this.SetProneLimit; }
Settings.IKOverrideFlyChange += this.SetIKOverrideFly;
Settings.IKOverrideJumpChange += this.SetIKOverrideJump; void OnDestroy()
Settings.DetectEmotesChange += this.SetDetectEmotes; {
Settings.FollowHipsChange += this.SetFollowHips; Settings.CrouchLimitChange -= this.SetCrouchLimit;
Settings.MassCenterChange += this.OnMassCenterChange; Settings.ProneLimitChange -= this.SetProneLimit;
} Settings.IKOverrideFlyChange -= this.SetIKOverrideFly;
Settings.IKOverrideJumpChange -= this.SetIKOverrideJump;
void OnDestroy() Settings.DetectEmotesChange -= this.SetDetectEmotes;
{ Settings.MassCenterChange -= this.OnMassCenterChange;
Settings.CrouchLimitChange -= this.SetCrouchLimit; }
Settings.ProneLimitChange -= this.SetProneLimit;
Settings.IKOverrideFlyChange -= this.SetIKOverrideFly; void Update()
Settings.IKOverrideJumpChange -= this.SetIKOverrideJump; {
Settings.DetectEmotesChange -= this.SetDetectEmotes; if(m_avatarReady)
Settings.FollowHipsChange -= this.SetFollowHips; {
Settings.MassCenterChange -= this.OnMassCenterChange; m_grounded = MovementSystem.Instance.IsGrounded();
} m_groundedRaw = MovementSystem.Instance.IsGroundedRaw();
m_moving = !Mathf.Approximately(MovementSystem.Instance.movementVector.magnitude, 0f);
void Update()
{ UpdateIKLimits();
if(m_avatarReady)
{ m_emoteActive = false;
m_grounded = MovementSystem.Instance.IsGrounded(); if(m_detectEmotes && (m_locomotionLayer >= 0))
m_groundedRaw = MovementSystem.Instance.IsGroundedRaw(); {
m_moving = !Mathf.Approximately(MovementSystem.Instance.movementVector.magnitude, 0f); AnimatorStateInfo l_animState = PlayerSetup.Instance._animator.GetCurrentAnimatorStateInfo(m_locomotionLayer);
m_emoteActive = (l_animState.tagHash == ms_emoteHash);
UpdateIKLimits(); }
if(m_avatarHips != null) if(m_parameters.Count > 0)
{ {
Vector4 l_hipsToPoint = (PlayerSetup.Instance.transform.GetMatrix().inverse * m_avatarHips.GetMatrix()) * ms_pointVector; foreach(AvatarParameter l_param in m_parameters)
m_hipsToPlayer.Set(l_hipsToPoint.x, 0f, l_hipsToPoint.z); l_param.Update(this);
} }
}
m_emoteActive = false; }
if(m_detectEmotes && (m_locomotionLayer >= 0))
{ // Game events
AnimatorStateInfo l_animState = PlayerSetup.Instance._animator.GetCurrentAnimatorStateInfo(m_locomotionLayer); internal void OnAvatarClear()
m_emoteActive = (l_animState.tagHash == ms_emoteHash); {
} m_vrIk = null;
m_locomotionLayer = -1;
if(m_parameters.Count > 0) m_grounded = false;
{ m_groundedRaw = false;
foreach(AvatarParameter l_param in m_parameters) m_avatarReady = false;
l_param.Update(this); m_avatarScale = 1f;
} m_locomotionOffset = Vector3.zero;
} m_emoteActive = false;
} m_moving = false;
m_locomotionOverride = false;
// Game events m_massCenter = Vector3.zero;
internal void OnAvatarClear() m_ikLimits = null;
{ m_parameters.Clear();
m_vrIk = null;
m_locomotionLayer = -1; PlayerSetup.Instance.avatarCrouchLimit = Mathf.Clamp01(Settings.CrouchLimit);
m_grounded = false; PlayerSetup.Instance.avatarProneLimit = Mathf.Clamp01(Settings.ProneLimit);
m_groundedRaw = false; }
m_avatarReady = false;
m_avatarScale = 1f; internal void OnSetupAvatar()
m_locomotionOffset = Vector3.zero; {
m_emoteActive = false; m_inVR = Utils.IsInVR();
m_moving = false; m_vrIk = PlayerSetup.Instance._avatar.GetComponent<VRIK>();
m_locomotionOverride = false; m_locomotionLayer = PlayerSetup.Instance._animator.GetLayerIndex("Locomotion/Emotes");
m_hipsToPlayer = Vector3.zero; m_avatarScale = Mathf.Abs(PlayerSetup.Instance._avatar.transform.localScale.y);
m_avatarHips = null;
m_massCenter = Vector3.zero; // Parse animator parameters
m_ikLimits = null; m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.Upright, PlayerSetup.Instance.animatorManager));
m_parameters.Clear(); m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.GroundedRaw, PlayerSetup.Instance.animatorManager));
m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.Moving, PlayerSetup.Instance.animatorManager));
PlayerSetup.Instance.avatarCrouchLimit = Mathf.Clamp01(Settings.CrouchLimit); m_parameters.RemoveAll(p => !p.IsValid());
PlayerSetup.Instance.avatarProneLimit = Mathf.Clamp01(Settings.ProneLimit);
} // Avatar custom IK limits
m_ikLimits = PlayerSetup.Instance._avatar.transform.Find("[IKLimits]");
internal void OnSetupAvatar() UpdateIKLimits();
{
m_inVR = Utils.IsInVR(); // Apply VRIK tweaks
m_vrIk = PlayerSetup.Instance._avatar.GetComponent<VRIK>(); if(m_vrIk != null)
m_locomotionLayer = PlayerSetup.Instance._animator.GetLayerIndex("Locomotion/Emotes"); {
m_avatarHips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips); m_locomotionOffset = m_vrIk.solver.locomotion.offset;
m_avatarScale = Mathf.Abs(PlayerSetup.Instance._avatar.transform.localScale.y); m_massCenter = m_locomotionOffset;
// Parse animator parameters if(m_vrIk.solver.HasToes())
m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.Upright, PlayerSetup.Instance.animatorManager)); {
m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.GroundedRaw, PlayerSetup.Instance.animatorManager)); Transform l_foot = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftFoot);
m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.Moving, PlayerSetup.Instance.animatorManager)); if(l_foot == null)
m_parameters.RemoveAll(p => !p.IsValid()); l_foot = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightFoot);
// Avatar custom IK limits Transform l_toe = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftToes);
m_ikLimits = PlayerSetup.Instance._avatar.transform.Find("[IKLimits]"); if(l_toe == null)
UpdateIKLimits(); l_toe = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightToes);
// Apply VRIK tweaks if((l_foot != null) && (l_toe != null))
if(m_vrIk != null) {
{ Vector3 l_footPos = (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_foot.GetMatrix()) * ms_pointVector;
m_locomotionOffset = m_vrIk.solver.locomotion.offset; Vector3 l_toePos = (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_toe.GetMatrix()) * ms_pointVector;
m_massCenter = m_locomotionOffset; m_massCenter = new Vector3(0f, 0f, l_toePos.z - l_footPos.z);
}
if(m_vrIk.solver.HasToes()) }
{
Transform l_foot = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftFoot); m_vrIk.solver.locomotion.offset = (Settings.MassCenter ? m_massCenter : m_locomotionOffset);
if(l_foot == null)
l_foot = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightFoot); m_vrIk.onPreSolverUpdate.AddListener(this.OnIKPreUpdate);
m_vrIk.onPostSolverUpdate.AddListener(this.OnIKPostUpdate);
Transform l_toe = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftToes); }
if(l_toe == null)
l_toe = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightToes); m_avatarReady = true;
}
if((l_foot != null) && (l_toe != null))
{ internal void OnCalibrate()
Vector3 l_footPos = (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_foot.GetMatrix()) * ms_pointVector; {
Vector3 l_toePos = (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_toe.GetMatrix()) * ms_pointVector; if(m_avatarReady && (m_vrIk != null) && (m_vrIk.solver.spine.pelvisTarget != null) && (m_vrIk.solver.leftLeg.target == null) && (m_vrIk.solver.rightLeg.target == null))
m_massCenter = new Vector3(0f, 0f, l_toePos.z - l_footPos.z); {
} // Do not consider 4PT as FBT (!!!)
} m_vrIk.solver.spine.bodyPosStiffness = 0.55f;
m_vrIk.solver.spine.bodyRotStiffness = 0.1f;
m_vrIk.solver.locomotion.offset = (Settings.MassCenter ? m_massCenter : m_locomotionOffset); m_vrIk.solver.spine.neckStiffness = 0.5f;
m_vrIk.solver.spine.chestClampWeight = 0.55f;
m_vrIk.onPreSolverUpdate.AddListener(this.OnIKPreUpdate); m_vrIk.solver.spine.moveBodyBackWhenCrouching = 0.5f;
m_vrIk.onPostSolverUpdate.AddListener(this.OnIKPostUpdate); m_vrIk.solver.spine.maxRootAngle = 25f;
} m_vrIk.fixTransforms = false;
m_avatarReady = true; BodySystem.isCalibratedAsFullBody = false;
} BodySystem.TrackingLeftLegEnabled = false;
BodySystem.TrackingRightLegEnabled = false;
internal void OnCalibrate() BodySystem.TrackingLocomotionEnabled = true;
{
if(m_avatarReady && (m_vrIk != null) && (m_vrIk.solver.spine.pelvisTarget != null) && (m_vrIk.solver.leftLeg.target == null) && (m_vrIk.solver.rightLeg.target == null)) IKSystem.Instance.applyOriginalHipRotation = true;
{ }
// Do not consider 4PT as FBT (!!!) }
m_vrIk.solver.spine.bodyPosStiffness = 0.55f;
m_vrIk.solver.spine.bodyRotStiffness = 0.1f; internal void OnPlayspaceScale()
m_vrIk.solver.spine.neckStiffness = 0.5f; {
m_vrIk.solver.spine.chestClampWeight = 0.55f; if((m_vrIk != null) && Settings.MassCenter)
m_vrIk.solver.spine.moveBodyBackWhenCrouching = 0.5f; m_vrIk.solver.locomotion.offset = m_massCenter * GetRelativeScale();
m_vrIk.solver.spine.maxRootAngle = 25f; }
m_vrIk.fixTransforms = false;
// IK events
BodySystem.isCalibratedAsFullBody = false; void OnIKPreUpdate()
BodySystem.TrackingLeftLegEnabled = false; {
BodySystem.TrackingRightLegEnabled = false; bool l_locomotionOverride = false;
BodySystem.TrackingLocomotionEnabled = true;
m_ikState.m_weight = m_vrIk.solver.IKPositionWeight;
IKSystem.Instance.applyOriginalHipRotation = true; m_ikState.m_locomotionWeight = m_vrIk.solver.locomotion.weight;
} m_ikState.m_plantFeet = m_vrIk.solver.plantFeet;
} m_ikState.m_bendNormalLeft = m_vrIk.solver.leftLeg.useAnimatedBendNormal;
m_ikState.m_bendNormalRight = m_vrIk.solver.rightLeg.useAnimatedBendNormal;
internal void OnPlayspaceScale()
{ if(m_detectEmotes && m_emoteActive)
if((m_vrIk != null) && Settings.MassCenter) m_vrIk.solver.IKPositionWeight = 0f;
m_vrIk.solver.locomotion.offset = m_massCenter * GetRelativeScale();
} if(!BodySystem.isCalibratedAsFullBody)
{
// IK events if(PlayerSetup.Instance.avatarUpright <= PlayerSetup.Instance.avatarCrouchLimit)
void OnIKPreUpdate() {
{ m_vrIk.solver.leftLeg.useAnimatedBendNormal = true;
bool l_locomotionOverride = false; m_vrIk.solver.rightLeg.useAnimatedBendNormal = true;
l_locomotionOverride = true;
m_ikState.m_weight = m_vrIk.solver.IKPositionWeight; }
m_ikState.m_locomotionWeight = m_vrIk.solver.locomotion.weight;
m_ikState.m_plantFeet = m_vrIk.solver.plantFeet; if(m_ikOverrideFly && MovementSystem.Instance.flying)
m_ikState.m_bendNormalLeft = m_vrIk.solver.leftLeg.useAnimatedBendNormal; {
m_ikState.m_bendNormalRight = m_vrIk.solver.rightLeg.useAnimatedBendNormal; m_vrIk.solver.locomotion.weight = 0f;
m_vrIk.solver.leftLeg.useAnimatedBendNormal = true;
if(m_detectEmotes && m_emoteActive) m_vrIk.solver.rightLeg.useAnimatedBendNormal = true;
m_vrIk.solver.IKPositionWeight = 0f; l_locomotionOverride = true;
}
if(!BodySystem.isCalibratedAsFullBody) if(m_ikOverrideJump && !m_grounded && !MovementSystem.Instance.flying)
{ {
if(PlayerSetup.Instance.avatarUpright <= PlayerSetup.Instance.avatarCrouchLimit) m_vrIk.solver.locomotion.weight = 0f;
{ m_vrIk.solver.leftLeg.useAnimatedBendNormal = true;
m_vrIk.solver.leftLeg.useAnimatedBendNormal = true; m_vrIk.solver.rightLeg.useAnimatedBendNormal = true;
m_vrIk.solver.rightLeg.useAnimatedBendNormal = true; l_locomotionOverride = true;
l_locomotionOverride = true; }
} }
if(m_ikOverrideFly && MovementSystem.Instance.flying) if(m_locomotionOverride && !l_locomotionOverride)
{ {
m_vrIk.solver.locomotion.weight = 0f; m_vrIk.solver.Reset();
m_vrIk.solver.leftLeg.useAnimatedBendNormal = true; if((IKSystem.VrikRootController != null) && !MovementSystem.Instance.sitting)
m_vrIk.solver.rightLeg.useAnimatedBendNormal = true; IKSystem.VrikRootController.enabled = true;
l_locomotionOverride = true; }
} m_locomotionOverride = l_locomotionOverride;
if(m_ikOverrideJump && !m_grounded && !MovementSystem.Instance.flying) }
{
m_vrIk.solver.locomotion.weight = 0f; void OnIKPostUpdate()
m_vrIk.solver.leftLeg.useAnimatedBendNormal = true; {
m_vrIk.solver.rightLeg.useAnimatedBendNormal = true; m_vrIk.solver.IKPositionWeight = m_ikState.m_weight;
l_locomotionOverride = true; m_vrIk.solver.locomotion.weight = m_ikState.m_locomotionWeight;
} m_vrIk.solver.plantFeet = m_ikState.m_plantFeet;
} m_vrIk.solver.leftLeg.useAnimatedBendNormal = m_ikState.m_bendNormalLeft;
m_vrIk.solver.rightLeg.useAnimatedBendNormal = m_ikState.m_bendNormalRight;
bool l_solverActive = !Mathf.Approximately(m_vrIk.solver.IKPositionWeight, 0f); }
if(l_locomotionOverride && l_solverActive && (m_followHips && !MovementSystem.Instance.sitting) && (!m_moving || (PlayerSetup.Instance.avatarUpright <= PlayerSetup.Instance.avatarCrouchLimit)) && m_inVR && !BodySystem.isCalibratedAsFullBody && !ModSupporter.SkipHipsOverride())
{ // Settings
m_vrIk.solver.plantFeet = false; internal void SetCrouchLimit(float p_value)
if((IKSystem.VrikRootController != null) && !MovementSystem.Instance.sitting) {
IKSystem.VrikRootController.enabled = false; if(m_ikLimits == null)
PlayerSetup.Instance._avatar.transform.localPosition = m_hipsToPlayer; PlayerSetup.Instance.avatarCrouchLimit = Mathf.Clamp01(p_value);
} }
internal void SetProneLimit(float p_value)
if(m_locomotionOverride && !l_locomotionOverride) {
{ if(m_ikLimits == null)
m_vrIk.solver.Reset(); PlayerSetup.Instance.avatarProneLimit = Mathf.Clamp01(p_value);
if((IKSystem.VrikRootController != null) && !MovementSystem.Instance.sitting) }
IKSystem.VrikRootController.enabled = true; internal void SetIKOverrideFly(bool p_state)
} {
m_locomotionOverride = l_locomotionOverride; m_ikOverrideFly = p_state;
} }
internal void SetIKOverrideJump(bool p_state)
void OnIKPostUpdate() {
{ m_ikOverrideJump = p_state;
m_vrIk.solver.IKPositionWeight = m_ikState.m_weight; }
m_vrIk.solver.locomotion.weight = m_ikState.m_locomotionWeight; internal void SetDetectEmotes(bool p_state)
m_vrIk.solver.plantFeet = m_ikState.m_plantFeet; {
m_vrIk.solver.leftLeg.useAnimatedBendNormal = m_ikState.m_bendNormalLeft; m_detectEmotes = p_state;
m_vrIk.solver.rightLeg.useAnimatedBendNormal = m_ikState.m_bendNormalRight; }
} void OnMassCenterChange(bool p_state)
{
// Settings if(m_vrIk != null)
internal void SetCrouchLimit(float p_value) m_vrIk.solver.locomotion.offset = (Settings.MassCenter ? (m_massCenter * GetRelativeScale()) : m_locomotionOffset);
{ }
if(m_ikLimits == null)
PlayerSetup.Instance.avatarCrouchLimit = Mathf.Clamp01(p_value); // Arbitrary
} float GetRelativeScale()
internal void SetProneLimit(float p_value) {
{ return ((m_avatarScale > 0f) ? (PlayerSetup.Instance._avatar.transform.localScale.y / m_avatarScale) : 0f);
if(m_ikLimits == null) }
PlayerSetup.Instance.avatarProneLimit = Mathf.Clamp01(p_value);
} void UpdateIKLimits()
internal void SetIKOverrideFly(bool p_state) {
{ if(m_ikLimits != null)
m_ikOverrideFly = p_state; {
} Vector3 l_values = m_ikLimits.localPosition;
internal void SetIKOverrideJump(bool p_state) PlayerSetup.Instance.avatarCrouchLimit = Mathf.Clamp01(l_values.x);
{ PlayerSetup.Instance.avatarProneLimit = Mathf.Clamp01(l_values.y);
m_ikOverrideJump = p_state; }
} }
internal void SetDetectEmotes(bool p_state)
{ // Parameters access
m_detectEmotes = p_state; public float GetUpright() => PlayerSetup.Instance.avatarUpright;
} public bool GetGroundedRaw() => m_groundedRaw;
internal void SetFollowHips(bool p_state) public bool GetMoving() => m_moving;
{ }
m_followHips = p_state; }
}
void OnMassCenterChange(bool p_state)
{
if(m_vrIk != null)
m_vrIk.solver.locomotion.offset = (Settings.MassCenter ? (m_massCenter * GetRelativeScale()) : m_locomotionOffset);
}
// Arbitrary
float GetRelativeScale()
{
return ((m_avatarScale > 0f) ? (PlayerSetup.Instance._avatar.transform.localScale.y / m_avatarScale) : 0f);
}
void UpdateIKLimits()
{
if(m_ikLimits != null)
{
Vector3 l_values = m_ikLimits.localPosition;
PlayerSetup.Instance.avatarCrouchLimit = Mathf.Clamp01(l_values.x);
PlayerSetup.Instance.avatarProneLimit = Mathf.Clamp01(l_values.y);
}
}
// Parameters access
public float GetUpright() => PlayerSetup.Instance.avatarUpright;
public bool GetGroundedRaw() => m_groundedRaw;
public bool GetMoving() => m_moving;
}
}

View file

@ -1,5 +1,4 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.3.3", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.3.4", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonOptionalDependencies("ml_prm", "ml_pmc")] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]

View file

@ -1,36 +1,33 @@
# Avatar Motion Tweaker # Avatar Motion Tweaker
This mod adds features for AAS animator and avatar locomotion behaviour. This mod adds features for AAS animator and avatar locomotion behaviour.
![](.github/img_01.png) ![](.github/img_01.png)
# Installation # Installation
* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) * Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader)
* Get [latest release DLL](../../../releases/latest): * Get [latest release DLL](../../../releases/latest):
* Put `ml_amt.dll` in `Mods` folder of game * Put `ml_amt.dll` in `Mods` folder of game
# Usage # Usage
Available mod's settings in `Settings - IK - Avatar Motion Tweaker`: Available mod's settings in `Settings - IK - Avatar Motion Tweaker`:
* **Crouch limit:** defines crouch limit; default value - `75`. * **Crouch limit:** defines crouch limit; default value - `75`.
* **Prone limit:** defines prone limit; default value - `40`. * **Prone limit:** defines prone limit; default value - `40`.
* **IK override while flying:** disables legs locomotion/autostep in fly mode; default value - `true`. * **IK override while flying:** disables legs locomotion/autostep in fly mode; default value - `true`.
* **IK override while jumping:** disables legs locomotion/autostep in jump; default value - `true`. * **IK override while jumping:** disables legs locomotion/autostep in jump; default value - `true`.
* **Follow hips on IK override:** adjusts avatar position to overcome animation snapping on IK override; default value - `true`. * **Detect animations emote tag:** disables avatar's IK entirely if current animator state has `Emote` tag; default value - `true`.
* Note: Works best with animations that have root transform position (XZ) based on center of mass. * Note: Created as example for [propoused game feature](https://feedback.abinteractive.net/p/disabling-vr-ik-for-emotes-via-animator-state-tag-7b80d963-053a-41c0-86ac-e3d53c61c1e2).
* Note: Made for four point tracking (head, hands and hips) in mind. * **Adjusted locomotion mass center:** automatically changes IK locomotion center if avatar has toe bones; default value - `true`.
* **Detect animations emote tag:** disables avatar's IK entirely if current animator state has `Emote` tag; default value - `true`. * Note: Compatible with [DesktopVRIK](https://github.com/NotAKidOnSteam/DesktopVRIK) and [FuckToes](https://github.com/NotAKidOnSteam/FuckToes).
* Note: Created as example for [propoused game feature](https://feedback.abinteractive.net/p/disabling-vr-ik-for-emotes-via-animator-state-tag-7b80d963-053a-41c0-86ac-e3d53c61c1e2).
* **Adjusted locomotion mass center:** automatically changes IK locomotion center if avatar has toe bones; default value - `true`. Available additional parameters for AAS animator:
* Note: Compatible with [DesktopVRIK](https://github.com/NotAKidOnSteam/DesktopVRIK) and [FuckToes](https://github.com/NotAKidOnSteam/FuckToes). * **`Upright`:** defines linear coefficient between current viewpoint height and avatar's viewpoint height; float, range - [0.0, 1.0].
* Note: Can be set as local-only (not synced) if starts with `#` character.
Available additional parameters for AAS animator: * Note: Shouldn't be used for transitions between poses in desktop mode. In desktop mode its value is driven by avatar animations. Use `CVR Parameter Stream` for detecting desktop/VR modes and change AAS animator transitions accordingly.
* **`Upright`:** defines linear coefficient between current viewpoint height and avatar's viewpoint height; float, range - [0.0, 1.0]. * **`GroundedRaw`:** defines instant grounding state of player instead of delayed default parameter `Grounded`; boolean.
* Note: Can be set as local-only (not synced) if starts with `#` character. * Note: Can be set as local-only (not synced) if starts with `#` character.
* Note: Shouldn't be used for transitions between poses in desktop mode. In desktop mode its value is driven by avatar animations. Use `CVR Parameter Stream` for detecting desktop/VR modes and change AAS animator transitions accordingly. * **`Moving`:** defines movement state of player; boolean.
* **`GroundedRaw`:** defines instant grounding state of player instead of delayed default parameter `Grounded`; boolean. * Note: Can be set as local-only (not synced) if starts with `#` character.
* Note: Can be set as local-only (not synced) if starts with `#` character.
* **`Moving`:** defines movement state of player; boolean. Additional mod's behaviour:
* Note: Can be set as local-only (not synced) if starts with `#` character. * Overrides and fixes IK behaviour in 4PT mode (head, hands and hips).
Additional mod's behaviour:
* Overrides and fixes IK behaviour in 4PT mode (head, hands and hips).
* Avatars can have controlled IK crouch and prone limits. For that create `[IKLimits]` GameObject parented to avatar's root. Its local X and Y positions will be used as crouch and prone limits respectively and can be changed via animations. Values should be in range of [0;1]. * Avatars can have controlled IK crouch and prone limits. For that create `[IKLimits]` GameObject parented to avatar's root. Its local X and Y positions will be used as crouch and prone limits respectively and can be changed via animations. Values should be in range of [0;1].

View file

@ -1,159 +1,148 @@
using ABI_RC.Core.InteractionSystem; using ABI_RC.Core.InteractionSystem;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace ml_amt namespace ml_amt
{ {
static class Settings static class Settings
{ {
enum ModSetting enum ModSetting
{ {
CrouchLimit, CrouchLimit,
ProneLimit, ProneLimit,
IKOverrideFly, IKOverrideFly,
IKOverrideJump, IKOverrideJump,
DetectEmotes, DetectEmotes,
FollowHips, FollowHips,
MassCenter MassCenter
}; };
public static float CrouchLimit { get; private set; } = 0.75f; public static float CrouchLimit { get; private set; } = 0.75f;
public static float ProneLimit { get; private set; } = 0.4f; public static float ProneLimit { get; private set; } = 0.4f;
public static bool IKOverrideFly { get; private set; } = true; public static bool IKOverrideFly { get; private set; } = true;
public static bool IKOverrideJump { get; private set; } = true; public static bool IKOverrideJump { get; private set; } = true;
public static bool DetectEmotes { get; private set; } = true; public static bool DetectEmotes { get; private set; } = true;
public static bool FollowHips { get; private set; } = true; public static bool MassCenter { get; private set; } = true;
public static bool MassCenter { get; private set; } = true;
static MelonLoader.MelonPreferences_Category ms_category = null;
static MelonLoader.MelonPreferences_Category ms_category = null; static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
static public event Action<float> CrouchLimitChange;
static public event Action<float> CrouchLimitChange; static public event Action<float> ProneLimitChange;
static public event Action<float> ProneLimitChange; static public event Action<bool> IKOverrideFlyChange;
static public event Action<bool> IKOverrideFlyChange; static public event Action<bool> IKOverrideJumpChange;
static public event Action<bool> IKOverrideJumpChange; static public event Action<bool> DetectEmotesChange;
static public event Action<bool> DetectEmotesChange; static public event Action<bool> MassCenterChange;
static public event Action<bool> FollowHipsChange;
static public event Action<bool> MassCenterChange; internal static void Init()
{
internal static void Init() ms_category = MelonLoader.MelonPreferences.CreateCategory("AMT", null, true);
{
ms_category = MelonLoader.MelonPreferences.CreateCategory("AMT", null, true); ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
{
ms_entries = new List<MelonLoader.MelonPreferences_Entry>() ms_category.CreateEntry(ModSetting.CrouchLimit.ToString(), (int)(CrouchLimit * 100f)),
{ ms_category.CreateEntry(ModSetting.ProneLimit.ToString(), (int)(ProneLimit * 100f)),
ms_category.CreateEntry(ModSetting.CrouchLimit.ToString(), (int)(CrouchLimit * 100f)), ms_category.CreateEntry(ModSetting.IKOverrideFly.ToString(), IKOverrideFly),
ms_category.CreateEntry(ModSetting.ProneLimit.ToString(), (int)(ProneLimit * 100f)), ms_category.CreateEntry(ModSetting.IKOverrideJump.ToString(), IKOverrideJump),
ms_category.CreateEntry(ModSetting.IKOverrideFly.ToString(), IKOverrideFly), ms_category.CreateEntry(ModSetting.DetectEmotes.ToString(), DetectEmotes),
ms_category.CreateEntry(ModSetting.IKOverrideJump.ToString(), IKOverrideJump), ms_category.CreateEntry(ModSetting.MassCenter.ToString(), MassCenter)
ms_category.CreateEntry(ModSetting.DetectEmotes.ToString(), DetectEmotes), };
ms_category.CreateEntry(ModSetting.FollowHips.ToString(), FollowHips),
ms_category.CreateEntry(ModSetting.MassCenter.ToString(), MassCenter) CrouchLimit = ((int)ms_entries[(int)ModSetting.CrouchLimit].BoxedValue) * 0.01f;
}; ProneLimit = ((int)ms_entries[(int)ModSetting.ProneLimit].BoxedValue) * 0.01f;
IKOverrideFly = (bool)ms_entries[(int)ModSetting.IKOverrideFly].BoxedValue;
CrouchLimit = ((int)ms_entries[(int)ModSetting.CrouchLimit].BoxedValue) * 0.01f; IKOverrideJump = (bool)ms_entries[(int)ModSetting.IKOverrideJump].BoxedValue;
ProneLimit = ((int)ms_entries[(int)ModSetting.ProneLimit].BoxedValue) * 0.01f; DetectEmotes = (bool)ms_entries[(int)ModSetting.DetectEmotes].BoxedValue;
IKOverrideFly = (bool)ms_entries[(int)ModSetting.IKOverrideFly].BoxedValue; MassCenter = (bool)ms_entries[(int)ModSetting.MassCenter].BoxedValue;
IKOverrideJump = (bool)ms_entries[(int)ModSetting.IKOverrideJump].BoxedValue;
DetectEmotes = (bool)ms_entries[(int)ModSetting.DetectEmotes].BoxedValue; MelonLoader.MelonCoroutines.Start(WaitMainMenuUi());
FollowHips = (bool)ms_entries[(int)ModSetting.FollowHips].BoxedValue; }
MassCenter = (bool)ms_entries[(int)ModSetting.MassCenter].BoxedValue;
static System.Collections.IEnumerator WaitMainMenuUi()
MelonLoader.MelonCoroutines.Start(WaitMainMenuUi()); {
} while(ViewManager.Instance == null)
yield return null;
static System.Collections.IEnumerator WaitMainMenuUi() while(ViewManager.Instance.gameMenuView == null)
{ yield return null;
while(ViewManager.Instance == null) while(ViewManager.Instance.gameMenuView.Listener == null)
yield return null; yield return null;
while(ViewManager.Instance.gameMenuView == null)
yield return null; ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () =>
while(ViewManager.Instance.gameMenuView.Listener == null) {
yield return null; ViewManager.Instance.gameMenuView.View.BindCall("OnSliderUpdate_" + ms_category.Identifier, new Action<string, string>(OnSliderUpdate));
ViewManager.Instance.gameMenuView.View.BindCall("OnToggleUpdate_" + ms_category.Identifier, new Action<string, string>(OnToggleUpdate));
ViewManager.Instance.gameMenuView.Listener.ReadyForBindings += () => };
{ ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) =>
ViewManager.Instance.gameMenuView.View.BindCall("OnSliderUpdate_" + ms_category.Identifier, new Action<string, string>(OnSliderUpdate)); {
ViewManager.Instance.gameMenuView.View.BindCall("OnToggleUpdate_" + ms_category.Identifier, new Action<string, string>(OnToggleUpdate)); ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mods_extension.js"));
}; ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mod_menu.js"));
ViewManager.Instance.gameMenuView.Listener.FinishLoad += (_) => foreach(var l_entry in ms_entries)
{ ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString());
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mods_extension.js")); };
ViewManager.Instance.gameMenuView.View.ExecuteScript(Scripts.GetEmbeddedScript("mod_menu.js")); }
foreach(var l_entry in ms_entries)
ViewManager.Instance.gameMenuView.View.TriggerEvent("updateModSetting", ms_category.Identifier, l_entry.DisplayName, l_entry.GetValueAsString()); static void OnSliderUpdate(string p_name, string p_value)
}; {
} if(Enum.TryParse(p_name, out ModSetting l_setting))
{
static void OnSliderUpdate(string p_name, string p_value) switch(l_setting)
{ {
if(Enum.TryParse(p_name, out ModSetting l_setting)) case ModSetting.CrouchLimit:
{ {
switch(l_setting) CrouchLimit = int.Parse(p_value) * 0.01f;
{ CrouchLimitChange?.Invoke(CrouchLimit);
case ModSetting.CrouchLimit: }
{ break;
CrouchLimit = int.Parse(p_value) * 0.01f;
CrouchLimitChange?.Invoke(CrouchLimit); case ModSetting.ProneLimit:
} {
break; ProneLimit = int.Parse(p_value) * 0.01f;
ProneLimitChange?.Invoke(ProneLimit);
case ModSetting.ProneLimit: }
{ break;
ProneLimit = int.Parse(p_value) * 0.01f; }
ProneLimitChange?.Invoke(ProneLimit);
} ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value);
break; }
} }
ms_entries[(int)l_setting].BoxedValue = int.Parse(p_value); static void OnToggleUpdate(string p_name, string p_value)
} {
} if(Enum.TryParse(p_name, out ModSetting l_setting))
{
static void OnToggleUpdate(string p_name, string p_value) switch(l_setting)
{ {
if(Enum.TryParse(p_name, out ModSetting l_setting)) case ModSetting.IKOverrideFly:
{ {
switch(l_setting) IKOverrideFly = bool.Parse(p_value);
{ IKOverrideFlyChange?.Invoke(IKOverrideFly);
case ModSetting.IKOverrideFly: }
{ break;
IKOverrideFly = bool.Parse(p_value);
IKOverrideFlyChange?.Invoke(IKOverrideFly); case ModSetting.IKOverrideJump:
} {
break; IKOverrideJump = bool.Parse(p_value);
IKOverrideJumpChange?.Invoke(IKOverrideJump);
case ModSetting.IKOverrideJump: }
{ break;
IKOverrideJump = bool.Parse(p_value);
IKOverrideJumpChange?.Invoke(IKOverrideJump); case ModSetting.DetectEmotes:
} {
break; DetectEmotes = bool.Parse(p_value);
DetectEmotesChange?.Invoke(DetectEmotes);
case ModSetting.DetectEmotes: }
{ break;
DetectEmotes = bool.Parse(p_value);
DetectEmotesChange?.Invoke(DetectEmotes); case ModSetting.MassCenter:
} {
break; MassCenter = bool.Parse(p_value);
MassCenterChange?.Invoke(MassCenter);
case ModSetting.FollowHips: }
{ break;
FollowHips = bool.Parse(p_value); }
FollowHipsChange?.Invoke(FollowHips);
} ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);
break; }
}
case ModSetting.MassCenter: }
{ }
MassCenter = bool.Parse(p_value);
MassCenterChange?.Invoke(MassCenter);
}
break;
}
ms_entries[(int)l_setting].BoxedValue = bool.Parse(p_value);
}
}
}
}

View file

@ -1,95 +1,87 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<Authors>SDraw</Authors> <Authors>SDraw</Authors>
<Company>None</Company> <Company>None</Company>
<Product>AvatarMotionTweaker</Product> <Product>AvatarMotionTweaker</Product>
<PackageId>AvatarMotionTweaker</PackageId> <PackageId>AvatarMotionTweaker</PackageId>
<Version>1.3.3</Version> <Version>1.3.4</Version>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<AssemblyName>ml_amt</AssemblyName> <AssemblyName>ml_amt</AssemblyName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<DebugType>none</DebugType> <DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols> <DebugSymbols>false</DebugSymbols>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks> <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="Test.cs" /> <Compile Remove="Test.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="AvatarMotionTweaker.json" /> <None Remove="AvatarMotionTweaker.json" />
<None Remove="resources\mod_menu.js" /> <None Remove="resources\mod_menu.js" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="resources\mod_menu.js" /> <EmbeddedResource Include="resources\mod_menu.js" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="..\js\mods_extension.js" Link="resources\mods_extension.js" /> <EmbeddedResource Include="..\js\mods_extension.js" Link="resources\mods_extension.js" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="0Harmony"> <Reference Include="0Harmony">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath> <HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="Assembly-CSharp"> <Reference Include="Assembly-CSharp">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="Assembly-CSharp-firstpass"> <Reference Include="Assembly-CSharp-firstpass">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="cohtml.Net"> <Reference Include="cohtml.Net">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="Cohtml.Runtime"> <Reference Include="Cohtml.Runtime">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="MelonLoader"> <Reference Include="MelonLoader">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath> <HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="ml_pmc"> <Reference Include="UnityEngine">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\Mods\ml_pmc.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="ml_prm"> <Reference Include="UnityEngine.AnimationModule">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\Mods\ml_prm.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="UnityEngine"> <Reference Include="UnityEngine.CoreModule">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.AnimationModule"> <Reference Include="UnityEngine.PhysicsModule">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.PhysicsModule.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.CoreModule"> </ItemGroup>
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
</Reference> <Exec Command="copy /y &quot;$(TargetPath)&quot; &quot;D:\Games\Steam\steamapps\common\ChilloutVR\Mods\&quot;" />
<Reference Include="UnityEngine.PhysicsModule"> </Target>
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.PhysicsModule.dll</HintPath>
<Private>false</Private> </Project>
</Reference>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="copy /y &quot;$(TargetPath)&quot; &quot;D:\Games\Steam\steamapps\common\ChilloutVR\Mods\&quot;" />
</Target>
</Project>

View file

@ -1,68 +1,61 @@
// Add own menu // Add own menu
{ {
let l_block = document.createElement('div'); let l_block = document.createElement('div');
l_block.innerHTML = ` l_block.innerHTML = `
<div class ="settings-subcategory"> <div class ="settings-subcategory">
<div class ="subcategory-name">Avatar Motion Tweaker</div> <div class ="subcategory-name">Avatar Motion Tweaker</div>
<div class ="subcategory-description"></div> <div class ="subcategory-description"></div>
</div> </div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Crouch limit: </div> <div class ="option-caption">Crouch limit: </div>
<div class ="option-input"> <div class ="option-input">
<div id="CrouchLimit" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="75"></div> <div id="CrouchLimit" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="75"></div>
</div> </div>
</div> </div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Prone limit: </div> <div class ="option-caption">Prone limit: </div>
<div class ="option-input"> <div class ="option-input">
<div id="ProneLimit" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="40"></div> <div id="ProneLimit" class ="inp_slider no-scroll" data-min="0" data-max="100" data-current="40"></div>
</div> </div>
</div> </div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">IK override while flying: </div> <div class ="option-caption">IK override while flying: </div>
<div class ="option-input"> <div class ="option-input">
<div id="IKOverrideFly" class ="inp_toggle no-scroll" data-current="true"></div> <div id="IKOverrideFly" class ="inp_toggle no-scroll" data-current="true"></div>
</div> </div>
</div> </div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">IK override while jumping: </div> <div class ="option-caption">Follow hips on IK override: </div>
<div class ="option-input"> <div class ="option-input">
<div id="IKOverrideJump" class ="inp_toggle no-scroll" data-current="true"></div> <div id="FollowHips" class ="inp_toggle no-scroll" data-current="true"></div>
</div> </div>
</div> </div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Follow hips on IK override: </div> <div class ="option-caption">Detect animations emote tag: </div>
<div class ="option-input"> <div class ="option-input">
<div id="FollowHips" class ="inp_toggle no-scroll" data-current="true"></div> <div id="DetectEmotes" class ="inp_toggle no-scroll" data-current="true"></div>
</div> </div>
</div> </div>
<div class ="row-wrapper"> <div class ="row-wrapper">
<div class ="option-caption">Detect animations emote tag: </div> <div class ="option-caption">Adjusted locomotion mass center: </div>
<div class ="option-input"> <div class ="option-input">
<div id="DetectEmotes" class ="inp_toggle no-scroll" data-current="true"></div> <div id="MassCenter" class ="inp_toggle no-scroll" data-current="true"></div>
</div> </div>
</div> </div>
`;
<div class ="row-wrapper"> document.getElementById('settings-ik').appendChild(l_block);
<div class ="option-caption">Adjusted locomotion mass center: </div>
<div class ="option-input"> // Toggles
<div id="MassCenter" class ="inp_toggle no-scroll" data-current="true"></div> for (let l_toggle of l_block.querySelectorAll('.inp_toggle'))
</div> modsExtension.addSetting('AMT', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_AMT'));
</div>
`; // Sliders
document.getElementById('settings-ik').appendChild(l_block); for (let l_slider of l_block.querySelectorAll('.inp_slider'))
modsExtension.addSetting('AMT', l_slider.id, modsExtension.createSlider(l_slider, 'OnSliderUpdate_AMT'));
// Toggles }
for (let l_toggle of l_block.querySelectorAll('.inp_toggle'))
modsExtension.addSetting('AMT', l_toggle.id, modsExtension.createToggle(l_toggle, 'OnToggleUpdate_AMT'));
// Sliders
for (let l_slider of l_block.querySelectorAll('.inp_slider'))
modsExtension.addSetting('AMT', l_slider.id, modsExtension.createSlider(l_slider, 'OnSliderUpdate_AMT'));
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.4.3", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonInfo(typeof(ml_lme.LeapMotionExtension), "LeapMotionExtension", "1.4.4", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
[assembly: MelonLoader.MelonOptionalDependencies("ml_pmc")] [assembly: MelonLoader.MelonOptionalDependencies("ml_pmc")]
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] [assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
[assembly: MelonLoader.MelonAdditionalCredits("NotAKidOnSteam")] [assembly: MelonLoader.MelonAdditionalCredits("NotAKidOnSteam")]

View file

@ -1,50 +1,50 @@
using ABI_RC.Core.Savior; using ABI_RC.Core.Savior;
using ABI_RC.Core.UI; using ABI_RC.Core.UI;
using ABI_RC.Systems.InputManagement; using ABI_RC.Systems.InputManagement;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
namespace ml_lme namespace ml_lme
{ {
static class Utils static class Utils
{ {
static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); static readonly FieldInfo ms_view = typeof(CohtmlControlledViewWrapper).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance);
public static bool IsInVR() => ((CheckVR.Instance != null) && CheckVR.Instance.hasVrDeviceLoaded); 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 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 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 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) 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); 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 ShowHUDNotification(string p_title, string p_message, string p_small = "", bool p_immediate = false) public static void ShowHUDNotification(string p_title, string p_message, string p_small = "", bool p_immediate = false)
{ {
if(CohtmlHud.Instance != null) if(CohtmlHud.Instance != null)
{ {
if(p_immediate) if(p_immediate)
CohtmlHud.Instance.ViewDropTextImmediate(p_title, p_message, p_small); CohtmlHud.Instance.ViewDropTextImmediate(p_title, p_message, p_small);
else else
CohtmlHud.Instance.ViewDropText(p_title, p_message, p_small); CohtmlHud.Instance.ViewDropText(p_title, p_message, p_small);
} }
} }
static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script); static public void ExecuteScript(this CohtmlControlledViewWrapper p_instance, string p_script) => ((cohtml.Net.View)ms_view.GetValue(p_instance)).ExecuteScript(p_script);
public static void Swap<T>(ref T lhs, ref T rhs) public static void Swap<T>(ref T lhs, ref T rhs)
{ {
T temp = lhs; T temp = lhs;
lhs = rhs; lhs = rhs;
rhs = temp; rhs = temp;
} }
public static float InverseLerpUnclamped(float a, float b, float value) public static float InverseLerpUnclamped(float a, float b, float value)
{ {
if(a != b) if(a != b)
return (value - a) / (b - a); return (value - a) / (b - a);
return 0f; return 0f;
} }
} }
} }

View file

@ -1,103 +1,103 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<PackageId>LeapMotionExtension</PackageId> <PackageId>LeapMotionExtension</PackageId>
<Version>1.4.3</Version> <Version>1.4.4</Version>
<Authors>SDraw</Authors> <Authors>SDraw</Authors>
<Company>None</Company> <Company>None</Company>
<Product>LeapMotionExtension</Product> <Product>LeapMotionExtension</Product>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DebugType>none</DebugType> <DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols> <DebugSymbols>false</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="vendor\LeapSDK\**" /> <Compile Remove="vendor\LeapSDK\**" />
<EmbeddedResource Remove="vendor\LeapSDK\**" /> <EmbeddedResource Remove="vendor\LeapSDK\**" />
<None Remove="vendor\LeapSDK\**" /> <None Remove="vendor\LeapSDK\**" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="LeapMotionExtension.json" /> <None Remove="LeapMotionExtension.json" />
<None Remove="resources\leapmotion_controller.asset" /> <None Remove="resources\leapmotion_controller.asset" />
<None Remove="resources\leapmotion_hands.asset" /> <None Remove="resources\leapmotion_hands.asset" />
<None Remove="resources\mod_menu.js" /> <None Remove="resources\mod_menu.js" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="resources\leapmotion_controller.asset" /> <EmbeddedResource Include="resources\leapmotion_controller.asset" />
<EmbeddedResource Include="resources\leapmotion_hands.asset" /> <EmbeddedResource Include="resources\leapmotion_hands.asset" />
<EmbeddedResource Include="resources\mod_menu.js" /> <EmbeddedResource Include="resources\mod_menu.js" />
<EmbeddedResource Include="vendor\LeapSDK\lib\x64\LeapC.dll"> <EmbeddedResource Include="vendor\LeapSDK\lib\x64\LeapC.dll">
<Link>resources/LeapC.dll</Link> <Link>resources/LeapC.dll</Link>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="0Harmony"> <Reference Include="0Harmony">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath> <HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="Assembly-CSharp"> <Reference Include="Assembly-CSharp">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="Assembly-CSharp-firstpass"> <Reference Include="Assembly-CSharp-firstpass">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="cohtml.Net"> <Reference Include="cohtml.Net">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="Cohtml.Runtime"> <Reference Include="Cohtml.Runtime">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="MelonLoader"> <Reference Include="MelonLoader">
<HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath> <HintPath>D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="ml_pmc"> <Reference Include="ml_pmc">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\Mods\ml_pmc.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\Mods\ml_pmc.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="UnityEngine"> <Reference Include="UnityEngine">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.AnimationModule"> <Reference Include="UnityEngine.AnimationModule">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.AssetBundleModule"> <Reference Include="UnityEngine.AssetBundleModule">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AssetBundleModule.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AssetBundleModule.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.CoreModule"> <Reference Include="UnityEngine.CoreModule">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.XRModule"> <Reference Include="UnityEngine.XRModule">
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.XRModule.dll</HintPath> <HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.XRModule.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="vendor\LeapCSharp\" /> <Folder Include="vendor\LeapCSharp\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="..\js\mods_extension.js" Link="resources\mods_extension.js" /> <EmbeddedResource Include="..\js\mods_extension.js" Link="resources\mods_extension.js" />
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="copy /y &quot;$(TargetPath)&quot; &quot;D:\Games\Steam\steamapps\common\ChilloutVR\Mods\&quot;" /> <Exec Command="copy /y &quot;$(TargetPath)&quot; &quot;D:\Games\Steam\steamapps\common\ChilloutVR\Mods\&quot;" />
</Target> </Target>
</Project> </Project>

View file

@ -1,299 +1,299 @@
using ABI.CCK.Components; using ABI.CCK.Components;
using ABI_RC.Core.InteractionSystem; using ABI_RC.Core.InteractionSystem;
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using RootMotion.FinalIK; using RootMotion.FinalIK;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
namespace ml_pam namespace ml_pam
{ {
[DisallowMultipleComponent] [DisallowMultipleComponent]
class ArmMover : MonoBehaviour class ArmMover : MonoBehaviour
{ {
const float c_offsetLimit = 0.5f; const float c_offsetLimit = 0.5f;
static readonly float[] ms_tposeMuscles = typeof(ABI_RC.Systems.IK.SubSystems.BodySystem).GetField("TPoseMuscles", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null) as float[]; static readonly float[] ms_tposeMuscles = typeof(ABI_RC.Systems.IK.SubSystems.BodySystem).GetField("TPoseMuscles", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null) as float[];
static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f); static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f);
static readonly Quaternion ms_offsetRight = Quaternion.Euler(0f, 0f, 90f); static readonly Quaternion ms_offsetRight = Quaternion.Euler(0f, 0f, 90f);
static readonly Quaternion ms_offsetRightDesktop = Quaternion.Euler(0f, 270f, 0f); static readonly Quaternion ms_offsetRightDesktop = Quaternion.Euler(0f, 270f, 0f);
static readonly Quaternion ms_palmToLeft = Quaternion.Euler(0f, 0f, -90f); static readonly Quaternion ms_palmToLeft = Quaternion.Euler(0f, 0f, -90f);
bool m_inVR = false; bool m_inVR = false;
VRIK m_vrIK = null; VRIK m_vrIK = null;
Vector2 m_armWeight = Vector2.zero; Vector2 m_armWeight = Vector2.zero;
Transform m_origRightHand = null; Transform m_origRightHand = null;
float m_playspaceScale = 1f; float m_playspaceScale = 1f;
bool m_enabled = true; bool m_enabled = true;
ArmIK m_armIK = null; ArmIK m_armIK = null;
Transform m_target = null; Transform m_target = null;
Transform m_rotationTarget = null; Transform m_rotationTarget = null;
CVRPickupObject m_pickup = null; CVRPickupObject m_pickup = null;
Matrix4x4 m_offset = Matrix4x4.identity; Matrix4x4 m_offset = Matrix4x4.identity;
bool m_targetActive = false; bool m_targetActive = false;
// Unity events // Unity events
void Start() void Start()
{ {
m_inVR = Utils.IsInVR(); m_inVR = Utils.IsInVR();
m_target = new GameObject("ArmPickupTarget").transform; m_target = new GameObject("ArmPickupTarget").transform;
m_target.parent = PlayerSetup.Instance.GetActiveCamera().transform; m_target.parent = PlayerSetup.Instance.GetActiveCamera().transform;
m_target.localPosition = Vector3.zero; m_target.localPosition = Vector3.zero;
m_target.localRotation = Quaternion.identity; m_target.localRotation = Quaternion.identity;
m_rotationTarget = new GameObject("RotationTarget").transform; m_rotationTarget = new GameObject("RotationTarget").transform;
m_rotationTarget.parent = m_target; m_rotationTarget.parent = m_target;
m_rotationTarget.localPosition = new Vector3(c_offsetLimit * Settings.GrabOffset, 0f, 0f); m_rotationTarget.localPosition = new Vector3(c_offsetLimit * Settings.GrabOffset, 0f, 0f);
m_rotationTarget.localRotation = Quaternion.identity; m_rotationTarget.localRotation = Quaternion.identity;
m_enabled = Settings.Enabled; m_enabled = Settings.Enabled;
Settings.EnabledChange += this.SetEnabled; Settings.EnabledChange += this.SetEnabled;
Settings.GrabOffsetChange += this.SetGrabOffset; Settings.GrabOffsetChange += this.SetGrabOffset;
} }
void OnDestroy() void OnDestroy()
{ {
Settings.EnabledChange -= this.SetEnabled; Settings.EnabledChange -= this.SetEnabled;
Settings.GrabOffsetChange -= this.SetGrabOffset; Settings.GrabOffsetChange -= this.SetGrabOffset;
} }
void Update() void Update()
{ {
if(m_enabled && !ReferenceEquals(m_pickup, null)) if(m_enabled && !ReferenceEquals(m_pickup, null))
{ {
if(m_pickup != null) if(m_pickup != null)
{ {
Matrix4x4 l_result = m_pickup.transform.GetMatrix() * m_offset; Matrix4x4 l_result = m_pickup.transform.GetMatrix() * m_offset;
m_target.position = l_result * ms_pointVector; m_target.position = l_result * ms_pointVector;
} }
else else
this.OnPickupDrop(m_pickup); this.OnPickupDrop(m_pickup);
} }
} }
// IK updates // IK updates
void OnIKPreUpdate() void OnIKPreUpdate()
{ {
m_armWeight.Set(m_vrIK.solver.rightArm.positionWeight, m_vrIK.solver.rightArm.rotationWeight); m_armWeight.Set(m_vrIK.solver.rightArm.positionWeight, m_vrIK.solver.rightArm.rotationWeight);
if(m_targetActive && (Mathf.Approximately(m_armWeight.x, 0f) || Mathf.Approximately(m_armWeight.y, 0f))) if(m_targetActive && (Mathf.Approximately(m_armWeight.x, 0f) || Mathf.Approximately(m_armWeight.y, 0f)))
{ {
m_vrIK.solver.rightArm.positionWeight = 1f; m_vrIK.solver.rightArm.positionWeight = 1f;
m_vrIK.solver.rightArm.rotationWeight = 1f; m_vrIK.solver.rightArm.rotationWeight = 1f;
} }
} }
void OnIKPostUpdate() void OnIKPostUpdate()
{ {
m_vrIK.solver.rightArm.positionWeight = m_armWeight.x; m_vrIK.solver.rightArm.positionWeight = m_armWeight.x;
m_vrIK.solver.rightArm.rotationWeight = m_armWeight.y; m_vrIK.solver.rightArm.rotationWeight = m_armWeight.y;
} }
// Settings // Settings
void SetEnabled(bool p_state) void SetEnabled(bool p_state)
{ {
m_enabled = p_state; m_enabled = p_state;
RefreshArmIK(); RefreshArmIK();
if(m_enabled) if(m_enabled)
RestorePickup(); RestorePickup();
else else
RestoreVRIK(); RestoreVRIK();
} }
void SetGrabOffset(float p_value) void SetGrabOffset(float p_value)
{ {
if(m_rotationTarget != null) if(m_rotationTarget != null)
m_rotationTarget.localPosition = new Vector3(c_offsetLimit * m_playspaceScale * p_value, 0f, 0f); m_rotationTarget.localPosition = new Vector3(c_offsetLimit * m_playspaceScale * p_value, 0f, 0f);
} }
// Game events // Game events
internal void OnAvatarClear() internal void OnAvatarClear()
{ {
m_vrIK = null; m_vrIK = null;
m_origRightHand = null; m_origRightHand = null;
m_armIK = null; m_armIK = null;
m_targetActive = false; m_targetActive = false;
} }
internal void OnAvatarSetup() internal void OnAvatarSetup()
{ {
// Recheck if user could switch to VR // Recheck if user could switch to VR
if(m_inVR != Utils.IsInVR()) if(m_inVR != Utils.IsInVR())
{ {
m_target.parent = PlayerSetup.Instance.GetActiveCamera().transform; m_target.parent = PlayerSetup.Instance.GetActiveCamera().transform;
m_target.localPosition = Vector3.zero; m_target.localPosition = Vector3.zero;
m_target.localRotation = Quaternion.identity; m_target.localRotation = Quaternion.identity;
} }
m_inVR = Utils.IsInVR(); m_inVR = Utils.IsInVR();
m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>(); m_vrIK = PlayerSetup.Instance._animator.GetComponent<VRIK>();
if(PlayerSetup.Instance._animator.isHuman) if(PlayerSetup.Instance._animator.isHuman)
{ {
Vector3 l_hipsPos = Vector3.zero; Vector3 l_hipsPos = Vector3.zero;
Transform l_hips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips); Transform l_hips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips);
if(l_hips != null) if(l_hips != null)
l_hipsPos = l_hips.localPosition; l_hipsPos = l_hips.localPosition;
HumanPose l_currentPose = new HumanPose(); HumanPose l_currentPose = new HumanPose();
HumanPoseHandler l_poseHandler = null; HumanPoseHandler l_poseHandler = null;
if(!m_inVR) if(!m_inVR)
{ {
l_poseHandler = new HumanPoseHandler(PlayerSetup.Instance._animator.avatar, PlayerSetup.Instance._avatar.transform); l_poseHandler = new HumanPoseHandler(PlayerSetup.Instance._animator.avatar, PlayerSetup.Instance._avatar.transform);
l_poseHandler.GetHumanPose(ref l_currentPose); l_poseHandler.GetHumanPose(ref l_currentPose);
HumanPose l_tPose = new HumanPose HumanPose l_tPose = new HumanPose
{ {
bodyPosition = l_currentPose.bodyPosition, bodyPosition = l_currentPose.bodyPosition,
bodyRotation = l_currentPose.bodyRotation, bodyRotation = l_currentPose.bodyRotation,
muscles = new float[l_currentPose.muscles.Length] muscles = new float[l_currentPose.muscles.Length]
}; };
for(int i = 0; i < l_tPose.muscles.Length; i++) for(int i = 0; i < l_tPose.muscles.Length; i++)
l_tPose.muscles[i] = ms_tposeMuscles[i]; l_tPose.muscles[i] = ms_tposeMuscles[i];
l_poseHandler.SetHumanPose(ref l_tPose); l_poseHandler.SetHumanPose(ref l_tPose);
} }
Transform l_hand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand); Transform l_hand = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightHand);
if(l_hand != null) if(l_hand != null)
m_rotationTarget.localRotation = (ms_palmToLeft * (m_inVR ? ms_offsetRight : ms_offsetRightDesktop)) * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_hand.GetMatrix()).rotation; m_rotationTarget.localRotation = (ms_palmToLeft * (m_inVR ? ms_offsetRight : ms_offsetRightDesktop)) * (PlayerSetup.Instance._avatar.transform.GetMatrix().inverse * l_hand.GetMatrix()).rotation;
if(m_vrIK == null) if(m_vrIK == null)
{ {
Transform l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.UpperChest); Transform l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.UpperChest);
if(l_chest == null) if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Chest); l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Chest);
if(l_chest == null) if(l_chest == null)
l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Spine); l_chest = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Spine);
m_armIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>(); m_armIK = PlayerSetup.Instance._avatar.AddComponent<ArmIK>();
m_armIK.solver.isLeft = false; m_armIK.solver.isLeft = false;
m_armIK.solver.SetChain( m_armIK.solver.SetChain(
l_chest, l_chest,
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightShoulder), PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightShoulder),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightUpperArm), PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightUpperArm),
PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightLowerArm), PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.RightLowerArm),
l_hand, l_hand,
PlayerSetup.Instance._animator.transform PlayerSetup.Instance._animator.transform
); );
m_armIK.solver.arm.target = m_rotationTarget; m_armIK.solver.arm.target = m_rotationTarget;
m_armIK.solver.arm.positionWeight = 1f; m_armIK.solver.arm.positionWeight = 1f;
m_armIK.solver.arm.rotationWeight = 1f; m_armIK.solver.arm.rotationWeight = 1f;
m_armIK.solver.IKPositionWeight = 0f; m_armIK.solver.IKPositionWeight = 0f;
m_armIK.solver.IKRotationWeight = 0f; m_armIK.solver.IKRotationWeight = 0f;
m_armIK.enabled = m_enabled; m_armIK.enabled = m_enabled;
} }
else else
{ {
m_origRightHand = m_vrIK.solver.rightArm.target; m_origRightHand = m_vrIK.solver.rightArm.target;
m_vrIK.solver.OnPreUpdate += this.OnIKPreUpdate; m_vrIK.solver.OnPreUpdate += this.OnIKPreUpdate;
m_vrIK.solver.OnPostUpdate += this.OnIKPostUpdate; m_vrIK.solver.OnPostUpdate += this.OnIKPostUpdate;
} }
l_poseHandler?.SetHumanPose(ref l_currentPose); l_poseHandler?.SetHumanPose(ref l_currentPose);
l_poseHandler?.Dispose(); l_poseHandler?.Dispose();
if(l_hips != null) if(l_hips != null)
l_hips.localPosition = l_hipsPos; l_hips.localPosition = l_hipsPos;
} }
if(m_enabled) if(m_enabled)
RestorePickup(); RestorePickup();
} }
internal void OnPickupGrab(CVRPickupObject p_pickup, ControllerRay p_ray, Vector3 p_hit) internal void OnPickupGrab(CVRPickupObject p_pickup, ControllerRay p_ray, Vector3 p_hit)
{ {
if(p_ray == ViewManager.Instance.desktopControllerRay) if(p_ray == ViewManager.Instance.desktopControllerRay)
{ {
m_pickup = p_pickup; m_pickup = p_pickup;
// Set offsets // Set offsets
if(m_pickup.gripType == CVRPickupObject.GripType.Origin) if(m_pickup.gripType == CVRPickupObject.GripType.Origin)
{ {
if(m_pickup.ikReference != null) if(m_pickup.ikReference != null)
m_offset = (m_pickup.transform.GetMatrix().inverse * m_pickup.ikReference.GetMatrix()); m_offset = (m_pickup.transform.GetMatrix().inverse * m_pickup.ikReference.GetMatrix());
else else
{ {
if(m_pickup.gripOrigin != null) if(m_pickup.gripOrigin != null)
m_offset = m_pickup.transform.GetMatrix().inverse * m_pickup.gripOrigin.GetMatrix(); m_offset = m_pickup.transform.GetMatrix().inverse * m_pickup.gripOrigin.GetMatrix();
} }
} }
else else
m_offset = m_pickup.transform.GetMatrix().inverse * Matrix4x4.Translate(p_hit); m_offset = m_pickup.transform.GetMatrix().inverse * Matrix4x4.Translate(p_hit);
if(m_enabled) if(m_enabled)
{ {
if((m_vrIK != null) && !m_targetActive) if((m_vrIK != null) && !m_targetActive)
{ {
m_vrIK.solver.rightArm.target = m_rotationTarget; m_vrIK.solver.rightArm.target = m_rotationTarget;
m_targetActive = true; m_targetActive = true;
} }
if(m_armIK != null) if(m_armIK != null)
{ {
m_armIK.solver.IKPositionWeight = 1f; m_armIK.solver.IKPositionWeight = 1f;
m_armIK.solver.IKRotationWeight = 1f; m_armIK.solver.IKRotationWeight = 1f;
} }
} }
} }
} }
internal void OnPickupDrop(CVRPickupObject p_pickup) internal void OnPickupDrop(CVRPickupObject p_pickup)
{ {
if(m_pickup == p_pickup) if(m_pickup == p_pickup)
{ {
m_pickup = null; m_pickup = null;
if(m_enabled) if(m_enabled)
{ {
RestoreVRIK(); RestoreVRIK();
if(m_armIK != null) if(m_armIK != null)
{ {
m_armIK.solver.IKPositionWeight = 0f; m_armIK.solver.IKPositionWeight = 0f;
m_armIK.solver.IKRotationWeight = 0f; m_armIK.solver.IKRotationWeight = 0f;
} }
} }
} }
} }
internal void OnPlayspaceScale(float p_relation) internal void OnPlayspaceScale(float p_relation)
{ {
m_playspaceScale = p_relation; m_playspaceScale = p_relation;
SetGrabOffset(Settings.GrabOffset); SetGrabOffset(Settings.GrabOffset);
} }
// Arbitrary // Arbitrary
void RestorePickup() void RestorePickup()
{ {
if((m_vrIK != null) && (m_pickup != null)) if((m_vrIK != null) && (m_pickup != null))
{ {
m_vrIK.solver.rightArm.target = m_rotationTarget; m_vrIK.solver.rightArm.target = m_rotationTarget;
m_targetActive = true; m_targetActive = true;
} }
if((m_armIK != null) && (m_pickup != null)) if((m_armIK != null) && (m_pickup != null))
{ {
m_armIK.solver.IKPositionWeight = 1f; m_armIK.solver.IKPositionWeight = 1f;
m_armIK.solver.IKRotationWeight = 1f; m_armIK.solver.IKRotationWeight = 1f;
} }
} }
void RestoreVRIK() void RestoreVRIK()
{ {
if((m_vrIK != null) && m_targetActive) if((m_vrIK != null) && m_targetActive)
{ {
m_vrIK.solver.rightArm.target = m_origRightHand; m_vrIK.solver.rightArm.target = m_origRightHand;
m_targetActive = false; m_targetActive = false;
} }
} }
void RefreshArmIK() void RefreshArmIK()
{ {
if(m_armIK != null) if(m_armIK != null)
m_armIK.enabled = m_enabled; m_armIK.enabled = m_enabled;
} }
} }
} }

View file

@ -1,79 +1,79 @@
using ABI.CCK.Components; using ABI.CCK.Components;
using ABI_RC.Core.Player; using ABI_RC.Core.Player;
using ABI_RC.Core.Savior; using ABI_RC.Core.Savior;
using ABI_RC.Systems.InputManagement; using ABI_RC.Systems.InputManagement;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
namespace ml_pmc namespace ml_pmc
{ {
static class Utils static class Utils
{ {
static readonly (int, int)[] ms_sideMuscles = new (int, int)[] 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), (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), (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), (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) (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 }; 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 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 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 IsWorldSafe() => ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying);
public static bool IsCombatSafe() => ((CombatSystem.Instance == null) || !CombatSystem.Instance.isDown); public static bool IsCombatSafe() => ((CombatSystem.Instance == null) || !CombatSystem.Instance.isDown);
public static float GetWorldMovementLimit() public static float GetWorldMovementLimit()
{ {
float l_result = 1f; float l_result = 1f;
if(CVRWorld.Instance != null) if(CVRWorld.Instance != null)
{ {
l_result = CVRWorld.Instance.baseMovementSpeed; l_result = CVRWorld.Instance.baseMovementSpeed;
l_result *= CVRWorld.Instance.sprintMultiplier; l_result *= CVRWorld.Instance.sprintMultiplier;
l_result *= CVRWorld.Instance.inAirMovementMultiplier; l_result *= CVRWorld.Instance.inAirMovementMultiplier;
l_result *= CVRWorld.Instance.flyMultiplier; l_result *= CVRWorld.Instance.flyMultiplier;
} }
return l_result; return l_result;
} }
public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false) 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); 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) public static void CopyTo(this HumanPose p_source, ref HumanPose p_target)
{ {
p_target.bodyPosition = p_source.bodyPosition; p_target.bodyPosition = p_source.bodyPosition;
p_target.bodyRotation = p_source.bodyRotation; p_target.bodyRotation = p_source.bodyRotation;
int l_count = Mathf.Min(p_source.muscles.Length, p_target.muscles.Length); int l_count = Mathf.Min(p_source.muscles.Length, p_target.muscles.Length);
for(int i = 0; i < l_count; i++) for(int i = 0; i < l_count; i++)
p_target.muscles[i] = p_source.muscles[i]; p_target.muscles[i] = p_source.muscles[i];
} }
public static void MirrorPose(ref HumanPose p_pose) public static void MirrorPose(ref HumanPose p_pose)
{ {
int l_count = p_pose.muscles.Length; int l_count = p_pose.muscles.Length;
foreach(var l_pair in ms_sideMuscles) foreach(var l_pair in ms_sideMuscles)
{ {
if((l_count > l_pair.Item1) && (l_count > l_pair.Item2)) if((l_count > l_pair.Item1) && (l_count > l_pair.Item2))
{ {
float l_temp = p_pose.muscles[l_pair.Item1]; 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.Item1] = p_pose.muscles[l_pair.Item2];
p_pose.muscles[l_pair.Item2] = l_temp; p_pose.muscles[l_pair.Item2] = l_temp;
} }
} }
foreach(int l_index in ms_centralMuscles) foreach(int l_index in ms_centralMuscles)
{ {
if(l_count > l_index) if(l_count > l_index)
p_pose.muscles[l_index] *= -1f; p_pose.muscles[l_index] *= -1f;
} }
p_pose.bodyRotation.x *= -1f; p_pose.bodyRotation.x *= -1f;
p_pose.bodyRotation.w *= -1f; p_pose.bodyRotation.w *= -1f;
p_pose.bodyPosition.x *= -1f; p_pose.bodyPosition.x *= -1f;
} }
} }
} }

View file

@ -1,56 +1,56 @@
using ABI_RC.Systems.InputManagement; using ABI_RC.Systems.InputManagement;
using ABI_RC.Systems.InputManagement.XR; using ABI_RC.Systems.InputManagement.XR;
using System; using System;
using System.Reflection; using System.Reflection;
namespace ml_vei namespace ml_vei
{ {
public class ViveExtendedInput : MelonLoader.MelonMod public class ViveExtendedInput : MelonLoader.MelonMod
{ {
public override void OnInitializeMelon() public override void OnInitializeMelon()
{ {
Settings.Init(); Settings.Init();
HarmonyInstance.Patch( HarmonyInstance.Patch(
typeof(CVRXRModule).GetMethod("Update_Gestures_Vive", BindingFlags.NonPublic | BindingFlags.Instance), typeof(CVRXRModule).GetMethod("Update_Gestures_Vive", BindingFlags.NonPublic | BindingFlags.Instance),
null, null,
new HarmonyLib.HarmonyMethod(typeof(ViveExtendedInput).GetMethod(nameof(OnViveGesturesUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) new HarmonyLib.HarmonyMethod(typeof(ViveExtendedInput).GetMethod(nameof(OnViveGesturesUpdate_Postfix), BindingFlags.Static | BindingFlags.NonPublic))
); );
} }
static void OnViveGesturesUpdate_Postfix(ref CVRXRModule __instance) static void OnViveGesturesUpdate_Postfix(ref CVRXRModule __instance)
{ {
try try
{ {
if(Settings.Gestures) if(Settings.Gestures)
{ {
float l_mag = ((!__instance.HasEmoteOverride) ? __instance.Primary2DAxis : __instance.EmoteOverride).magnitude; float l_mag = ((!__instance.HasEmoteOverride) ? __instance.Primary2DAxis : __instance.EmoteOverride).magnitude;
if(__instance.ViveDirectionPressed && (l_mag >= CVRInputManager.VrViveGestureDeadZone)) if(__instance.ViveDirectionPressed && (l_mag >= CVRInputManager.VrViveGestureDeadZone))
{ {
if(Settings.GripTrigger) if(Settings.GripTrigger)
{ {
switch(Settings.AxisPriority) switch(Settings.AxisPriority)
{ {
case Settings.PriorityAxis.Grip: case Settings.PriorityAxis.Grip:
__instance.GestureRaw = ((__instance.Grip > 0.5f) ? -1f : __instance.Trigger); __instance.GestureRaw = ((__instance.Grip > 0.5f) ? -1f : __instance.Trigger);
break; break;
case Settings.PriorityAxis.Trigger: case Settings.PriorityAxis.Trigger:
__instance.GestureRaw = (!UnityEngine.Mathf.Approximately(__instance.Trigger, 0f) ? __instance.Trigger : ((__instance.Grip > 0.5f) ? -1f : 0f)); __instance.GestureRaw = (!UnityEngine.Mathf.Approximately(__instance.Trigger, 0f) ? __instance.Trigger : ((__instance.Grip > 0.5f) ? -1f : 0f));
break; break;
} }
} }
else else
__instance.GestureRaw = 0f; __instance.GestureRaw = 0f;
__instance.Gesture = __instance.GestureRaw; __instance.Gesture = __instance.GestureRaw;
} }
} }
} }
catch(Exception e) catch(Exception e)
{ {
MelonLoader.MelonLogger.Error(e); MelonLoader.MelonLogger.Error(e);
} }
} }
} }
} }