diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 3b915e7..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 SDraw - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/ml_aci/.github/img_01.png b/ml_aci/.github/img_01.png deleted file mode 100644 index decfe41..0000000 Binary files a/ml_aci/.github/img_01.png and /dev/null differ diff --git a/ml_aci/Main.cs b/ml_aci/Main.cs deleted file mode 100644 index cc8711a..0000000 --- a/ml_aci/Main.cs +++ /dev/null @@ -1,58 +0,0 @@ -using ABI_RC.Core.EventSystem; -using ABI_RC.Core.InteractionSystem; -using ABI_RC.Core.Networking; -using ABI_RC.Core.Util; -using DarkRift; -using System.Reflection; - -namespace ml_aci -{ - public class AvatarChangeInfo : MelonLoader.MelonMod - { - public override void OnInitializeMelon() - { - HarmonyInstance.Patch( - typeof(AssetManagement).GetMethod(nameof(AssetManagement.LoadLocalAvatar)), - null, - new HarmonyLib.HarmonyMethod(typeof(AvatarChangeInfo).GetMethod(nameof(OnLocalAvatarLoad), BindingFlags.NonPublic | BindingFlags.Static)) - ); - - HarmonyInstance.Patch( - typeof(CVRSyncHelper).GetMethod(nameof(CVRSyncHelper.SpawnProp)), - null, - new HarmonyLib.HarmonyMethod(typeof(AvatarChangeInfo).GetMethod(nameof(OnPropSpawned), BindingFlags.NonPublic | BindingFlags.Static)) - ); - } - - static void OnLocalAvatarLoad() - { - try - { - if(ViewManager.Instance != null) - ViewManager.Instance.TriggerPushNotification("Avatar changed", 1f); - } - catch(System.Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - - static void OnPropSpawned() - { - try - { - if(ViewManager.Instance != null) - { - if((NetworkManager.Instance != null) && (NetworkManager.Instance.GameNetwork.ConnectionState == ConnectionState.Connected)) - ViewManager.Instance.TriggerPushNotification("Prop spawned", 1f); - else - ViewManager.Instance.TriggerAlert("Prop Error", "Not connected to live instance", -1, true); - } - } - catch(System.Exception e) - { - MelonLoader.MelonLogger.Error(e); - } - } - } -} diff --git a/ml_aci/README.md b/ml_aci/README.md deleted file mode 100644 index fba8935..0000000 --- a/ml_aci/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Avatar Change Info -This mod shows simple main menu popup upon local player avatar change and prop spawn. - -![](.github/img_01.png) - -# Installation -* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) -* Get [latest release DLL](../../../releases/latest): - * Put `ml_aci.dll` in `Mods` folder of game diff --git a/ml_aci/ml_aci.csproj b/ml_aci/ml_aci.csproj deleted file mode 100644 index 25e1641..0000000 --- a/ml_aci/ml_aci.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - - Debug - AnyCPU - {1B5ACA07-6266-4C9A-BA30-D4BBE6634846} - Library - Properties - ml_aci - ml_aci - v4.7.2 - 512 - true - - - true - bin\x64\Debug\ - DEBUG;TRACE - full - x64 - prompt - MinimumRecommendedRules.ruleset - - - bin\x64\Release\ - TRACE - true - pdbonly - x64 - prompt - MinimumRecommendedRules.ruleset - - - - False - F:\games\Steam\common\ChilloutVR\MelonLoader\0Harmony.dll - False - - - False - F:\games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll - False - - - False - - - False - F:\games\Steam\common\ChilloutVR\MelonLoader\MelonLoader.dll - False - - - - - - - - - - False - False - - - - - - - - - - - - - copy /y "$(TargetPath)" "C:\Games\Steam\common\ChilloutVR\Mods\" - - \ No newline at end of file diff --git a/ml_aci/ml_aci.csproj.user b/ml_aci/ml_aci.csproj.user deleted file mode 100644 index 2539084..0000000 --- a/ml_aci/ml_aci.csproj.user +++ /dev/null @@ -1,6 +0,0 @@ - - - - C:\Games\Steam\common\ChilloutVR\MelonLoader\;C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\ - - \ No newline at end of file diff --git a/ml_amt/AvatarParameter.cs b/ml_amt/AvatarParameter.cs index 3349104..7bec1d3 100644 --- a/ml_amt/AvatarParameter.cs +++ b/ml_amt/AvatarParameter.cs @@ -1,4 +1,6 @@ -using ABI_RC.Core.Player; +using ABI_RC.Core; +using System.Text.RegularExpressions; +using UnityEngine; namespace ml_amt { @@ -11,24 +13,30 @@ namespace ml_amt Moving } - public enum ParameterSyncType - { - Synced, - Local - } + readonly ParameterType m_type; + readonly string m_name; + readonly int m_hash = 0; + readonly bool m_sync; + readonly AnimatorControllerParameterType m_innerType; + readonly CVRAnimatorManager m_manager = null; - public readonly ParameterType m_type; - public readonly ParameterSyncType m_sync; - public readonly string m_name; - public readonly int m_hash; // For local only - - - public AvatarParameter(ParameterType p_type, string p_name, ParameterSyncType p_sync = ParameterSyncType.Synced, int p_hash = 0) + public AvatarParameter(ParameterType p_type, CVRAnimatorManager p_manager) { m_type = p_type; - m_sync = p_sync; - m_name = p_name; - m_hash = p_hash; + m_name = p_type.ToString(); + m_manager = p_manager; + + Regex l_regex = new Regex("^#?" + m_name + '$'); + foreach(var l_param in m_manager.animator.parameters) + { + if(l_regex.IsMatch(l_param.name)) + { + m_hash = l_param.nameHash; + m_sync = (l_param.name[0] != '#'); + m_innerType = l_param.type; + break; + } + } } public void Update(MotionTweaker p_tweaker) @@ -49,29 +57,28 @@ namespace ml_amt } } + public bool IsValid() => (m_hash != 0); + public ParameterType GetParameterType() => m_type; + void SetFloat(float p_value) { - switch(m_sync) + if(m_innerType == AnimatorControllerParameterType.Float) { - case ParameterSyncType.Local: - PlayerSetup.Instance._animator.SetFloat(m_hash, p_value); - break; - case ParameterSyncType.Synced: - PlayerSetup.Instance.animatorManager.SetAnimatorParameterFloat(m_name, p_value); - break; + if(m_sync) + m_manager.SetAnimatorParameterFloat(m_name, p_value); + else + m_manager.animator.SetFloat(m_hash, p_value); } } void SetBoolean(bool p_value) { - switch(m_sync) + if(m_innerType == AnimatorControllerParameterType.Bool) { - case ParameterSyncType.Local: - PlayerSetup.Instance._animator.SetBool(m_hash, p_value); - break; - case ParameterSyncType.Synced: - PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool(m_name, p_value); - break; + if(m_sync) + m_manager.SetAnimatorParameterBool(m_name, p_value); + else + m_manager.animator.SetBool(m_hash, p_value); } } } diff --git a/ml_amt/Fixes/AnimatorAnalyzer.cs b/ml_amt/Fixes/AnimatorAnalyzer.cs new file mode 100644 index 0000000..36900f8 --- /dev/null +++ b/ml_amt/Fixes/AnimatorAnalyzer.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace ml_amt.Fixes +{ + class AnimatorAnalyzer + { + bool m_enabled = true; + List m_parameters = null; + + public void AnalyzeFrom(Animator p_animator) + { + m_enabled = p_animator.enabled; + m_parameters = p_animator.parameters?.ToList(); + + if(m_parameters != null) + { + foreach(var l_param in m_parameters) + { + switch(l_param.type) + { + case AnimatorControllerParameterType.Bool: + case AnimatorControllerParameterType.Trigger: + l_param.defaultBool = p_animator.GetBool(l_param.nameHash); + break; + case AnimatorControllerParameterType.Float: + l_param.defaultFloat = p_animator.GetFloat(l_param.nameHash); + break; + case AnimatorControllerParameterType.Int: + l_param.defaultInt = p_animator.GetInteger(l_param.nameHash); + break; + + } + } + } + } + + public void ApplyTo(Animator p_animator) + { + p_animator.enabled = m_enabled; + + if(m_parameters != null) + { + foreach(var l_param in m_parameters) + { + switch(l_param.type) + { + case AnimatorControllerParameterType.Bool: + p_animator.SetBool(l_param.nameHash, l_param.defaultBool); + break; + case AnimatorControllerParameterType.Float: + p_animator.SetFloat(l_param.nameHash, l_param.defaultFloat); + break; + case AnimatorControllerParameterType.Int: + p_animator.SetInteger(l_param.nameHash, l_param.defaultInt); + break; + case AnimatorControllerParameterType.Trigger: + { + if(l_param.defaultBool) + p_animator.SetTrigger(l_param.nameHash); + } + break; + } + } + } + } + + public bool IsEnabled() => m_enabled; + } +} diff --git a/ml_amt/Fixes/AnimatorOverrideControllerFix.cs b/ml_amt/Fixes/AnimatorOverrideControllerFix.cs new file mode 100644 index 0000000..d375d51 --- /dev/null +++ b/ml_amt/Fixes/AnimatorOverrideControllerFix.cs @@ -0,0 +1,60 @@ +using ABI_RC.Core; +using System; +using System.Reflection; + +namespace ml_amt.Fixes +{ + static class AnimatorOverrideControllerFix + { + internal static void Init(HarmonyLib.Harmony p_instance) + { + // AAS overriding fix + p_instance.Patch( + typeof(CVRAnimatorManager).GetMethod(nameof(CVRAnimatorManager.SetOverrideAnimation)), + new HarmonyLib.HarmonyMethod(typeof(AnimatorOverrideControllerFix).GetMethod(nameof(OnOverride_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), + new HarmonyLib.HarmonyMethod(typeof(AnimatorOverrideControllerFix).GetMethod(nameof(OnOverride_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + p_instance.Patch( + typeof(CVRAnimatorManager).GetMethod(nameof(CVRAnimatorManager.RestoreOverrideAnimation)), + new HarmonyLib.HarmonyMethod(typeof(AnimatorOverrideControllerFix).GetMethod(nameof(OnOverride_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), + new HarmonyLib.HarmonyMethod(typeof(AnimatorOverrideControllerFix).GetMethod(nameof(OnOverride_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + } + + // AnimatorOverrideController runtime animation replacement fix + static void OnOverride_Prefix(ref CVRAnimatorManager __instance, out AnimatorAnalyzer __state) + { + __state = new AnimatorAnalyzer(); + try + { + if(Settings.OverrideFix && (__instance.animator != null)) + { + __state.AnalyzeFrom(__instance.animator); + if(__state.IsEnabled()) + __instance.animator.enabled = false; + __instance.animator.WriteDefaultValues(); + } + } + catch(Exception l_exception) + { + MelonLoader.MelonLogger.Error(l_exception); + } + } + static void OnOverride_Postfix(ref CVRAnimatorManager __instance, AnimatorAnalyzer __state) + { + try + { + if(Settings.OverrideFix && (__instance.animator != null)) + { + __state.ApplyTo(__instance.animator); + if(__state.IsEnabled()) + __instance.animator.Update(0f); + } + } + catch(Exception l_exception) + { + MelonLoader.MelonLogger.Error(l_exception); + } + } + } +} diff --git a/ml_amt/Fixes/FBTDetectionFix.cs b/ml_amt/Fixes/FBTDetectionFix.cs new file mode 100644 index 0000000..0dbb6e4 --- /dev/null +++ b/ml_amt/Fixes/FBTDetectionFix.cs @@ -0,0 +1,57 @@ +using ABI.CCK.Components; +using ABI_RC.Core.Player; +using ABI_RC.Systems.IK.SubSystems; +using System.Reflection; + +namespace ml_amt.Fixes +{ + static class FBTDetectionFix + { + static readonly MethodInfo[] ms_fbtDetouredMethods = + { + typeof(PlayerSetup).GetMethod("Update", BindingFlags.NonPublic | BindingFlags.Instance), + typeof(PlayerSetup).GetMethod("FixedUpdate", BindingFlags.NonPublic | BindingFlags.Instance), + typeof(PlayerSetup).GetMethod("UpdatePlayerAvatarMovementData", BindingFlags.NonPublic | BindingFlags.Instance), + typeof(CVRParameterStreamEntry).GetMethod(nameof(CVRParameterStreamEntry.CheckUpdate)) + }; + + static bool ms_fbtDetour = false; + + internal static void Init(HarmonyLib.Harmony p_instance) + { + // FBT detour + p_instance.Patch( + typeof(BodySystem).GetMethod(nameof(BodySystem.FBTAvailable)), + new HarmonyLib.HarmonyMethod(typeof(FBTDetectionFix).GetMethod(nameof(OnFBTAvailable_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), + null + ); + foreach(MethodInfo l_detoured in ms_fbtDetouredMethods) + { + p_instance.Patch( + l_detoured, + new HarmonyLib.HarmonyMethod(typeof(FBTDetectionFix).GetMethod(nameof(FBTDetour_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), + new HarmonyLib.HarmonyMethod(typeof(FBTDetectionFix).GetMethod(nameof(FBTDetour_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + } + } + + // FBT detection override + static void FBTDetour_Prefix() + { + ms_fbtDetour = true; + } + static void FBTDetour_Postfix() + { + ms_fbtDetour = false; + } + static bool OnFBTAvailable_Prefix(ref bool __result) + { + if(ms_fbtDetour && !BodySystem.isCalibratedAsFullBody) + { + __result = false; + return false; + } + return true; + } + } +} diff --git a/ml_amt/Fixes/MovementJumpFix.cs b/ml_amt/Fixes/MovementJumpFix.cs new file mode 100644 index 0000000..df18bc5 --- /dev/null +++ b/ml_amt/Fixes/MovementJumpFix.cs @@ -0,0 +1,114 @@ +using ABI.CCK.Components; +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; +using ABI_RC.Systems.MovementSystem; +using System; +using System.Collections; +using System.Reflection; +using UnityEngine; + +namespace ml_amt.Fixes +{ + static class MovementJumpFix + { + static FieldInfo ms_avatarHeight = typeof(PlayerSetup).GetField("_avatarHeight", BindingFlags.NonPublic | BindingFlags.Instance); + + static float ms_playerHeight = 1f; + + internal static void Init(HarmonyLib.Harmony p_instance) + { + p_instance.Patch( + typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.SetupAvatar)), + null, + new HarmonyLib.HarmonyMethod(typeof(MovementJumpFix).GetMethod(nameof(OnSetupAvatar_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + p_instance.Patch( + typeof(CVRWorld).GetMethod("SetupWorldRules", BindingFlags.NonPublic | BindingFlags.Instance), + null, + new HarmonyLib.HarmonyMethod(typeof(MovementJumpFix).GetMethod(nameof(OnWorldRulesSetup_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + p_instance.Patch( + typeof(PlayerSetup).GetMethod("SetupIKScaling", BindingFlags.NonPublic | BindingFlags.Instance), + null, + new HarmonyLib.HarmonyMethod(typeof(MovementJumpFix).GetMethod(nameof(OnSetupIKScaling_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + + Settings.ScaledJumpChange += OnScaledJumpChange; + MelonLoader.MelonCoroutines.Start(WaitForGameSettings()); + } + + static IEnumerator WaitForGameSettings() + { + while(MetaPort.Instance == null) + yield return null; + while(MetaPort.Instance.settings == null) + yield return null; + + ms_playerHeight = MetaPort.Instance.settings.GetSettingInt("GeneralPlayerHeight") * 0.01f; + MetaPort.Instance.settings.settingIntChanged.AddListener(OnGameSettingIntChange); + } + + // Patches + static void OnSetupAvatar_Postfix() + { + try + { + SetScaledJump(Settings.ScaledJump); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + static void OnWorldRulesSetup_Postfix() + { + try + { + SetScaledJump(Settings.ScaledJump); + } + catch(Exception e) + { + MelonLoader.MelonLogger.Error(e); + } + } + + static void OnSetupIKScaling_Postfix() + { + try + { + SetScaledJump(Settings.ScaledJump); + } + catch(Exception l_exception) + { + MelonLoader.MelonLogger.Error(l_exception); + } + } + + // Mod settings + static void OnScaledJumpChange(bool p_state) + { + SetScaledJump(p_state); + } + + // Game settings + static void OnGameSettingIntChange(string p_name, int p_value) + { + if(p_name == "GeneralPlayerHeight") + { + ms_playerHeight = p_value * 0.01f; + } + } + + // Arbitrary + static void SetScaledJump(bool p_state) + { + if(Utils.IsWorldSafe()) + { + if(p_state) + MovementSystem.Instance.jumpHeight = Mathf.Clamp(Utils.GetWorldJumpHeight() * ((float)ms_avatarHeight.GetValue(PlayerSetup.Instance) / ms_playerHeight), float.MinValue, Utils.GetWorldMovementLimit()); + else + MovementSystem.Instance.jumpHeight = Utils.GetWorldJumpHeight(); + } + } + } +} diff --git a/ml_amt/Fixes/PlayerColliderFix.cs b/ml_amt/Fixes/PlayerColliderFix.cs new file mode 100644 index 0000000..64eb722 --- /dev/null +++ b/ml_amt/Fixes/PlayerColliderFix.cs @@ -0,0 +1,123 @@ +using ABI_RC.Core.Player; +using ABI_RC.Systems.MovementSystem; +using System; +using System.Reflection; +using UnityEngine; + +namespace ml_amt.Fixes +{ + static class PlayerColliderFix + { + static FieldInfo ms_initialAvatarHeight = typeof(PlayerSetup).GetField("_initialAvatarHeight", BindingFlags.NonPublic | BindingFlags.Instance); + static FieldInfo ms_avatarHeight = typeof(PlayerSetup).GetField("_avatarHeight", BindingFlags.NonPublic | BindingFlags.Instance); + + internal static void Init(HarmonyLib.Harmony p_instance) + { + // Alternative collider height and radius + p_instance.Patch( + typeof(MovementSystem).GetMethod("UpdateCollider", BindingFlags.NonPublic | BindingFlags.Instance), + new HarmonyLib.HarmonyMethod(typeof(PlayerColliderFix).GetMethod(nameof(OnUpdateCollider_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), + null + ); + p_instance.Patch( + typeof(PlayerSetup).GetMethod("SetupIKScaling", BindingFlags.NonPublic | BindingFlags.Instance), + null, + new HarmonyLib.HarmonyMethod(typeof(PlayerColliderFix).GetMethod(nameof(OnSetupIKScaling_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) + ); + + Settings.CollisionScaleChange += OnCollisionScaleChange; + } + + // Alternative collider size + static bool OnUpdateCollider_Prefix( + ref MovementSystem __instance, + bool __0, // updateRadius + CharacterController ___controller, + float ____avatarHeight, + float ____avatarHeightFactor, + float ____minimumColliderRadius, + Vector3 ____colliderCenter + ) + { + if(!Settings.CollisionScale) + return true; + + try + { + if(___controller != null) + { + float l_scaledHeight = ____avatarHeight * ____avatarHeightFactor; + float l_newRadius = (__0 ? Mathf.Max(____minimumColliderRadius, l_scaledHeight / 6f) : ___controller.radius); + + float l_newHeight = Mathf.Max(l_scaledHeight, l_newRadius * 2f); + float l_currentHeight = ___controller.height; + + Vector3 l_newCenter = ____colliderCenter; + l_newCenter.y = (l_newHeight + 0.075f) * 0.5f; // Idk where 0.075f has come from + Vector3 l_currentCenter = ___controller.center; + + if(__0 || (Mathf.Abs(l_currentHeight - l_newHeight) > (l_currentHeight * 0.05f)) || (Vector3.Distance(l_currentCenter, l_newCenter) > (l_currentHeight * 0.05f))) + { + if(__0) + ___controller.radius = l_newRadius; + ___controller.height = l_newHeight; + ___controller.center = l_newCenter; + + __instance.groundDistance = l_newRadius; + + if(__instance.proxyCollider != null) + { + if(__0) + __instance.proxyCollider.radius = l_newRadius; + __instance.proxyCollider.height = l_newHeight; + __instance.proxyCollider.center = new Vector3(0f, l_newCenter.y, 0f); + } + + if(__instance.forceObject != null) + __instance.forceObject.transform.localScale = new Vector3(l_newRadius + 0.1f, l_newHeight, l_newRadius + 0.1f); + if(__instance.groundCheck != null) + __instance.groundCheck.localPosition = ____colliderCenter; + } + } + } + catch(Exception l_exception) + { + MelonLoader.MelonLogger.Error(l_exception); + } + + return false; + } + static void OnSetupIKScaling_Postfix( + ref PlayerSetup __instance, + float ____avatarHeight + ) + { + if(!Settings.CollisionScale) + return; + + try + { + __instance._movementSystem.UpdateAvatarHeight(Mathf.Clamp(____avatarHeight, 0.05f, float.MaxValue), true); + } + catch(Exception l_exception) + { + MelonLoader.MelonLogger.Error(l_exception); + } + } + + static void OnCollisionScaleChange(bool p_state) + { + try + { + if(p_state) + MovementSystem.Instance.UpdateAvatarHeight((float)ms_avatarHeight.GetValue(PlayerSetup.Instance), true); + else + MovementSystem.Instance.UpdateAvatarHeight((float)ms_initialAvatarHeight.GetValue(PlayerSetup.Instance), true); + } + catch(Exception l_exception) + { + MelonLoader.MelonLogger.Error(l_exception); + } + } + } +} diff --git a/ml_amt/Main.cs b/ml_amt/Main.cs index 881352a..cafb02a 100644 --- a/ml_amt/Main.cs +++ b/ml_amt/Main.cs @@ -1,30 +1,18 @@ using ABI.CCK.Components; -using ABI_RC.Core; using ABI_RC.Core.Player; using ABI_RC.Systems.IK.SubSystems; -using ABI_RC.Systems.MovementSystem; +using System; using System.Collections; using System.Reflection; -using UnityEngine; namespace ml_amt { public class AvatarMotionTweaker : MelonLoader.MelonMod { - static readonly MethodInfo[] ms_fbtDetouredMethods = - { - typeof(PlayerSetup).GetMethod("Update", BindingFlags.NonPublic | BindingFlags.Instance), - typeof(PlayerSetup).GetMethod("FixedUpdate", BindingFlags.NonPublic | BindingFlags.Instance), - typeof(PlayerSetup).GetMethod("UpdatePlayerAvatarMovementData", BindingFlags.NonPublic | BindingFlags.Instance), - typeof(CVRParameterStreamEntry).GetMethod(nameof(CVRParameterStreamEntry.CheckUpdate)) - }; - static AvatarMotionTweaker ms_instance = null; MotionTweaker m_localTweaker = null; - static bool ms_fbtDetour = false; - public override void OnInitializeMelon() { if(ms_instance == null) @@ -52,41 +40,15 @@ namespace ml_amt null, new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnPlayspaceScale_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) ); + - // FBT detour - HarmonyInstance.Patch( - typeof(BodySystem).GetMethod(nameof(BodySystem.FBTAvailable)), - new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnFBTAvailable_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), - null - ); - foreach(MethodInfo l_detoured in ms_fbtDetouredMethods) - { - HarmonyInstance.Patch( - l_detoured, - new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(FBTDetour_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), - new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(FBTDetour_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - } - - // Alternative collider height - HarmonyInstance.Patch( - typeof(MovementSystem).GetMethod("UpdateCollider", BindingFlags.NonPublic | BindingFlags.Instance), - new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnUpdateCollider_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), - null - ); - - // AAS overriding fix - HarmonyInstance.Patch( - typeof(CVRAnimatorManager).GetMethod(nameof(CVRAnimatorManager.SetOverrideAnimation)), - new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnOverride_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), - new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnOverride_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); - HarmonyInstance.Patch( - typeof(CVRAnimatorManager).GetMethod(nameof(CVRAnimatorManager.RestoreOverrideAnimation)), - new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnOverride_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), - new HarmonyLib.HarmonyMethod(typeof(AvatarMotionTweaker).GetMethod(nameof(OnOverride_Postfix), BindingFlags.Static | BindingFlags.NonPublic)) - ); + // Fixes + Fixes.AnimatorOverrideControllerFix.Init(HarmonyInstance); + Fixes.FBTDetectionFix.Init(HarmonyInstance); + Fixes.PlayerColliderFix.Init(HarmonyInstance); + Fixes.MovementJumpFix.Init(HarmonyInstance); + ModSupporter.Init(); MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer()); } @@ -124,7 +86,7 @@ namespace ml_amt if(m_localTweaker != null) m_localTweaker.OnAvatarClear(); } - catch(System.Exception l_exception) + catch(Exception l_exception) { MelonLoader.MelonLogger.Error(l_exception); } @@ -138,7 +100,7 @@ namespace ml_amt if(m_localTweaker != null) m_localTweaker.OnSetupAvatar(); } - catch(System.Exception l_exception) + catch(Exception l_exception) { MelonLoader.MelonLogger.Error(l_exception); } @@ -152,7 +114,7 @@ namespace ml_amt if(m_localTweaker != null) m_localTweaker.OnCalibrate(); } - catch(System.Exception l_exception) + catch(Exception l_exception) { MelonLoader.MelonLogger.Error(l_exception); } @@ -166,121 +128,7 @@ namespace ml_amt if(m_localTweaker != null) m_localTweaker.OnPlayspaceScale(); } - catch(System.Exception l_exception) - { - MelonLoader.MelonLogger.Error(l_exception); - } - } - - // FBT detection override - static void FBTDetour_Prefix() - { - ms_fbtDetour = true; - } - static void FBTDetour_Postfix() - { - ms_fbtDetour = false; - } - static bool OnFBTAvailable_Prefix(ref bool __result) - { - if(ms_fbtDetour && !BodySystem.isCalibratedAsFullBody) - { - __result = false; - return false; - } - - return true; - } - - // Alternative collider size - static bool OnUpdateCollider_Prefix( - ref MovementSystem __instance, - bool __0, // updateRadius - CharacterController ___controller, - float ____avatarHeight, - float ____avatarHeightFactor, - float ____minimumColliderRadius, - Vector3 ____colliderCenter - ) - { - if(!Settings.CollisionScale) - return true; - - try - { - if(___controller != null) - { - float l_scaledHeight = ____avatarHeight * ____avatarHeightFactor; - float l_newRadius = (__0 ? Mathf.Max(____minimumColliderRadius, l_scaledHeight / 6f) : ___controller.radius); - - float l_newHeight = Mathf.Max(l_scaledHeight, l_newRadius * 2f); - float l_currentHeight = ___controller.height; - - Vector3 l_newCenter = ____colliderCenter; - l_newCenter.y = (l_newHeight + 0.075f) * 0.5f; // Idk where 0.075f has come from - Vector3 l_currentCenter = ___controller.center; - - if(__0 || (Mathf.Abs(l_currentHeight - l_newHeight) > (l_currentHeight * 0.05f)) || (Vector3.Distance(l_currentCenter, l_newCenter) > (l_currentHeight * 0.05f))) - { - if(__0) - ___controller.radius = l_newRadius; - ___controller.height = l_newHeight; - ___controller.center = l_newCenter; - - __instance.groundDistance = l_newRadius; - - if(__instance.proxyCollider != null) - { - if(__0) - __instance.proxyCollider.radius = l_newRadius; - __instance.proxyCollider.height = l_newHeight; - __instance.proxyCollider.center = new Vector3(0f, l_newCenter.y, 0f); - } - - if(__instance.forceObject != null) - __instance.forceObject.transform.localScale = new Vector3(l_newRadius + 0.1f, l_newHeight, l_newRadius + 0.1f); - if(__instance.groundCheck != null) - __instance.groundCheck.localPosition = ____colliderCenter; - } - } - } - catch(System.Exception l_exception) - { - MelonLoader.MelonLogger.Error(l_exception); - } - - return false; - } - - static void OnOverride_Prefix(ref CVRAnimatorManager __instance, ref bool __state) - { - try - { - if(Settings.OverrideFix && (__instance.animator != null)) - { - __state = __instance.animator.enabled; - if(__state) - __instance.animator.enabled = false; - __instance.animator.WriteDefaultValues(); - } - } - catch(System.Exception l_exception) - { - MelonLoader.MelonLogger.Error(l_exception); - } - } - static void OnOverride_Postfix(ref CVRAnimatorManager __instance, bool __state) - { - try - { - if(Settings.OverrideFix && (__instance.animator != null)) - { - __instance.animator.enabled = __state; - if(__state) - __instance.animator.Update(0f); - } - } - catch(System.Exception l_exception) + catch(Exception l_exception) { MelonLoader.MelonLogger.Error(l_exception); } diff --git a/ml_amt/ModSupporter.cs b/ml_amt/ModSupporter.cs new file mode 100644 index 0000000..a453e86 --- /dev/null +++ b/ml_amt/ModSupporter.cs @@ -0,0 +1,49 @@ +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; + } + + + } +} diff --git a/ml_amt/MotionTweaker.cs b/ml_amt/MotionTweaker.cs index a0e7922..7fabdcb 100644 --- a/ml_amt/MotionTweaker.cs +++ b/ml_amt/MotionTweaker.cs @@ -1,10 +1,10 @@ using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; using ABI_RC.Systems.IK; using ABI_RC.Systems.IK.SubSystems; using ABI_RC.Systems.MovementSystem; using RootMotion.FinalIK; using System.Collections.Generic; -using System.Reflection; using UnityEngine; namespace ml_amt @@ -13,9 +13,6 @@ namespace ml_amt class MotionTweaker : MonoBehaviour { static readonly Vector4 ms_pointVector = new Vector4(0f, 0f, 0f, 1f); - static readonly FieldInfo ms_grounded = typeof(MovementSystem).GetField("_isGrounded", BindingFlags.NonPublic | BindingFlags.Instance); - static readonly FieldInfo ms_groundedRaw = typeof(MovementSystem).GetField("_isGroundedRaw", BindingFlags.NonPublic | BindingFlags.Instance); - static readonly FieldInfo ms_hasToes = typeof(IKSolverVR).GetField("hasToes", BindingFlags.NonPublic | BindingFlags.Instance); static readonly int ms_emoteHash = Animator.StringToHash("Emote"); enum PoseState @@ -35,8 +32,9 @@ namespace ml_amt bool m_bendNormalLeft = false; bool m_bendNormalRight = false; Transform m_avatarHips = null; - float m_viewPointHeight = 1f; + float m_avatarHeight = 1f; // Initial avatar view height bool m_inVR = false; + bool m_fbtAnimations = true; bool m_avatarReady = false; bool m_compatibleAvatar = false; @@ -93,6 +91,9 @@ namespace ml_amt Settings.FollowHipsChange += this.SetFollowHips; Settings.MassCenterChange += this.OnMassCenterChange; Settings.ScaledStepsChange += this.OnScaledStepsChange; + + m_fbtAnimations = MetaPort.Instance.settings.GetSettingsBool("GeneralEnableRunningAnimationFullBody"); + MetaPort.Instance.settings.settingBoolChanged.AddListener(this.OnGameSettingBoolChange); } void OnDestroy() @@ -108,20 +109,22 @@ namespace ml_amt Settings.DetectEmotesChange -= this.SetDetectEmotes; Settings.FollowHipsChange -= this.SetFollowHips; Settings.MassCenterChange -= this.OnMassCenterChange; + + MetaPort.Instance.settings.settingBoolChanged.RemoveListener(this.OnGameSettingBoolChange); } void Update() { if(m_avatarReady) { - m_grounded = (bool)ms_grounded.GetValue(MovementSystem.Instance); - m_groundedRaw = (bool)ms_groundedRaw.GetValue(MovementSystem.Instance); + m_grounded = MovementSystem.Instance.IsGrounded(); + m_groundedRaw = MovementSystem.Instance.IsGroundedRaw(); m_moving = !Mathf.Approximately(MovementSystem.Instance.movementVector.magnitude, 0f); // Update upright - Matrix4x4 l_hmdMatrix = PlayerSetup.Instance.transform.GetMatrix().inverse * (m_inVR ? PlayerSetup.Instance.vrHeadTracker.transform.GetMatrix() : PlayerSetup.Instance.desktopCameraRig.transform.GetMatrix()); + Matrix4x4 l_hmdMatrix = PlayerSetup.Instance.transform.GetMatrix().inverse * PlayerSetup.Instance.GetActiveCamera().transform.GetMatrix(); float l_currentHeight = Mathf.Clamp((l_hmdMatrix * ms_pointVector).y, 0f, float.MaxValue); - float l_avatarViewHeight = Mathf.Clamp(m_viewPointHeight * GetRelativeScale(), 0f, float.MaxValue); + float l_avatarViewHeight = Mathf.Clamp(m_avatarHeight * GetRelativeScale(), 0f, float.MaxValue); m_upright = Mathf.Clamp01((l_avatarViewHeight > 0f) ? (l_currentHeight / l_avatarViewHeight) : 0f); m_poseState = (m_upright <= Mathf.Min(m_proneLimit, m_crouchLimit)) ? PoseState.Proning : ((m_upright <= Mathf.Max(m_proneLimit, m_crouchLimit)) ? PoseState.Crouching : PoseState.Standing); @@ -147,8 +150,8 @@ namespace ml_amt if(m_poseTransitions) { - PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool("Crouching", (m_poseState == PoseState.Crouching) && !m_compatibleAvatar && !BodySystem.isCalibratedAsFullBody); - PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool("Prone", (m_poseState == PoseState.Proning) && !m_compatibleAvatar && !BodySystem.isCalibratedAsFullBody); + PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool("Crouching", (m_poseState == PoseState.Crouching) && !m_compatibleAvatar && (!BodySystem.isCalibratedAsFullBody || m_fbtAnimations)); + PlayerSetup.Instance.animatorManager.SetAnimatorParameterBool("Prone", (m_poseState == PoseState.Proning) && !m_compatibleAvatar && (!BodySystem.isCalibratedAsFullBody || m_fbtAnimations)); } } @@ -186,7 +189,7 @@ namespace ml_amt m_locomotionOverride = false; m_hipsToPlayer = Vector3.zero; m_avatarHips = null; - m_viewPointHeight = 1f; + m_avatarHeight = 1f; m_massCenter = Vector3.zero; m_stepDistance = Vector2.zero; m_parameters.Clear(); @@ -198,31 +201,15 @@ namespace ml_amt m_vrIk = PlayerSetup.Instance._avatar.GetComponent(); m_locomotionLayer = PlayerSetup.Instance._animator.GetLayerIndex("Locomotion/Emotes"); m_avatarHips = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.Hips); - m_viewPointHeight = PlayerSetup.Instance._avatar.GetComponent().viewPosition.y; + m_avatarHeight = PlayerSetup.Instance._avatar.GetComponent().viewPosition.y; // Parse animator parameters - AnimatorControllerParameter[] l_params = PlayerSetup.Instance._animator.parameters; - foreach(var l_param in l_params) - { - foreach(AvatarParameter.ParameterType l_enumParam in System.Enum.GetValues(typeof(AvatarParameter.ParameterType))) - { - if(l_param.name.Contains(l_enumParam.ToString()) && (m_parameters.FindIndex(p => p.m_type == l_enumParam) == -1)) - { - bool l_local = (l_param.name[0] == '#'); + m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.Upright, PlayerSetup.Instance.animatorManager)); + m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.GroundedRaw, PlayerSetup.Instance.animatorManager)); + m_parameters.Add(new AvatarParameter(AvatarParameter.ParameterType.Moving, PlayerSetup.Instance.animatorManager)); + m_parameters.RemoveAll(p => !p.IsValid()); - m_parameters.Add(new AvatarParameter( - l_enumParam, - l_param.name, - (l_local ? AvatarParameter.ParameterSyncType.Local : AvatarParameter.ParameterSyncType.Synced), - (l_local ? l_param.nameHash : 0) - )); - - break; - } - } - } - - m_compatibleAvatar = m_parameters.Exists(p => p.m_type == AvatarParameter.ParameterType.Upright); + m_compatibleAvatar = m_parameters.Exists(p => (p.GetParameterType() == AvatarParameter.ParameterType.Upright)); m_avatarScale = Mathf.Abs(PlayerSetup.Instance._avatar.transform.localScale.y); Transform l_customTransform = PlayerSetup.Instance._avatar.transform.Find("CrouchLimit"); @@ -239,7 +226,7 @@ namespace ml_amt m_locomotionOffset = m_vrIk.solver.locomotion.offset; m_massCenter = m_locomotionOffset; - if((bool)ms_hasToes.GetValue(m_vrIk.solver)) + if(m_vrIk.solver.HasToes()) { Transform l_foot = PlayerSetup.Instance._animator.GetBoneTransform(HumanBodyBones.LeftFoot); if(l_foot == null) @@ -340,10 +327,10 @@ namespace ml_amt } bool l_solverActive = !Mathf.Approximately(m_vrIk.solver.IKPositionWeight, 0f); - if(l_locomotionOverride && l_solverActive && m_followHips && (!m_moving || (m_poseState == PoseState.Proning)) && m_inVR && !BodySystem.isCalibratedAsFullBody) + if(l_locomotionOverride && l_solverActive && m_followHips && (!m_moving || (m_poseState == PoseState.Proning)) && m_inVR && !BodySystem.isCalibratedAsFullBody && !ModSupporter.SkipHipsOverride()) { m_vrIk.solver.plantFeet = false; - ABI_RC.Systems.IK.IKSystem.VrikRootController.enabled = false; + IKSystem.VrikRootController.enabled = false; PlayerSetup.Instance._avatar.transform.localPosition = m_hipsToPlayer; } @@ -441,6 +428,13 @@ namespace ml_amt } } + // Game settings + void OnGameSettingBoolChange(string p_name, bool p_state) + { + if(p_name == "GeneralEnableRunningAnimationFullBody") + m_fbtAnimations = p_state; + } + // Arbitrary float GetRelativeScale() { diff --git a/ml_amt/Properties/AssemblyInfo.cs b/ml_amt/Properties/AssemblyInfo.cs index dc4df48..aa484a0 100644 --- a/ml_amt/Properties/AssemblyInfo.cs +++ b/ml_amt/Properties/AssemblyInfo.cs @@ -1,4 +1,7 @@ -[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.2.4-ex", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] +using System.Reflection; + +[assembly: MelonLoader.MelonInfo(typeof(ml_amt.AvatarMotionTweaker), "AvatarMotionTweaker", "1.2.8", "SDraw", "https://github.com/SDraw/ml_mods_cvr")] [assembly: MelonLoader.MelonGame(null, "ChilloutVR")] +[assembly: MelonLoader.MelonOptionalDependencies("ml_prm", "ml_pmc")] [assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] [assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)] \ No newline at end of file diff --git a/ml_amt/Settings.cs b/ml_amt/Settings.cs index a317964..0f54401 100644 --- a/ml_amt/Settings.cs +++ b/ml_amt/Settings.cs @@ -1,4 +1,5 @@ using ABI_RC.Core.InteractionSystem; +using cohtml; using System; using System.Collections.Generic; @@ -20,6 +21,7 @@ namespace ml_amt FollowHips, CollisionScale, ScaledSteps, + ScaledJump, MassCenter, OverrideFix }; @@ -36,6 +38,7 @@ namespace ml_amt public static bool FollowHips { get; private set; } = true; public static bool MassCenter { get; private set; } = true; public static bool ScaledSteps { get; private set; } = true; + public static bool ScaledJump { get; private set; } = false; public static bool CollisionScale { get; private set; } = true; public static bool OverrideFix { get; private set; } = true; @@ -54,12 +57,13 @@ namespace ml_amt static public event Action FollowHipsChange; static public event Action MassCenterChange; static public event Action ScaledStepsChange; + static public event Action ScaledJumpChange; static public event Action CollisionScaleChange; static public event Action OverrideFixChange; internal static void Init() { - ms_category = MelonLoader.MelonPreferences.CreateCategory("AMT"); + ms_category = MelonLoader.MelonPreferences.CreateCategory("AMT", null, true); ms_entries = new List() { @@ -75,11 +79,26 @@ namespace ml_amt ms_category.CreateEntry(ModSetting.FollowHips.ToString(), FollowHips), ms_category.CreateEntry(ModSetting.MassCenter.ToString(), MassCenter), ms_category.CreateEntry(ModSetting.ScaledSteps.ToString(), ScaledSteps), + ms_category.CreateEntry(ModSetting.ScaledJump.ToString(), ScaledJump), ms_category.CreateEntry(ModSetting.CollisionScale.ToString(), CollisionScale), ms_category.CreateEntry(ModSetting.OverrideFix.ToString(), OverrideFix) }; - Load(); + IKOverrideCrouch = (bool)ms_entries[(int)ModSetting.IKOverrideCrouch].BoxedValue; + CrouchLimit = ((int)ms_entries[(int)ModSetting.CrouchLimit].BoxedValue) * 0.01f; + IKOverrideProne = (bool)ms_entries[(int)ModSetting.IKOverrideProne].BoxedValue; + ProneLimit = ((int)ms_entries[(int)ModSetting.ProneLimit].BoxedValue) * 0.01f; + PoseTransitions = (bool)ms_entries[(int)ModSetting.PoseTransitions].BoxedValue; + AdjustedMovement = (bool)ms_entries[(int)ModSetting.AdjustedMovement].BoxedValue; + IKOverrideFly = (bool)ms_entries[(int)ModSetting.IKOverrideFly].BoxedValue; + IKOverrideJump = (bool)ms_entries[(int)ModSetting.IKOverrideJump].BoxedValue; + DetectEmotes = (bool)ms_entries[(int)ModSetting.DetectEmotes].BoxedValue; + FollowHips = (bool)ms_entries[(int)ModSetting.FollowHips].BoxedValue; + MassCenter = (bool)ms_entries[(int)ModSetting.MassCenter].BoxedValue; + ScaledSteps = (bool)ms_entries[(int)ModSetting.ScaledSteps].BoxedValue; + ScaledJump = (bool)ms_entries[(int)ModSetting.ScaledJump].BoxedValue; + CollisionScale = (bool)ms_entries[(int)ModSetting.CollisionScale].BoxedValue; + OverrideFix = (bool)ms_entries[(int)ModSetting.OverrideFix].BoxedValue; MelonLoader.MelonCoroutines.Start(WaitMainMenuUi()); } @@ -106,24 +125,6 @@ namespace ml_amt }; } - static void Load() - { - IKOverrideCrouch = (bool)ms_entries[(int)ModSetting.IKOverrideCrouch].BoxedValue; - CrouchLimit = ((int)ms_entries[(int)ModSetting.CrouchLimit].BoxedValue) * 0.01f; - IKOverrideProne = (bool)ms_entries[(int)ModSetting.IKOverrideProne].BoxedValue; - ProneLimit = ((int)ms_entries[(int)ModSetting.ProneLimit].BoxedValue) * 0.01f; - PoseTransitions = (bool)ms_entries[(int)ModSetting.PoseTransitions].BoxedValue; - AdjustedMovement = (bool)ms_entries[(int)ModSetting.AdjustedMovement].BoxedValue; - IKOverrideFly = (bool)ms_entries[(int)ModSetting.IKOverrideFly].BoxedValue; - IKOverrideJump = (bool)ms_entries[(int)ModSetting.IKOverrideJump].BoxedValue; - DetectEmotes = (bool)ms_entries[(int)ModSetting.DetectEmotes].BoxedValue; - FollowHips = (bool)ms_entries[(int)ModSetting.FollowHips].BoxedValue; - MassCenter = (bool)ms_entries[(int)ModSetting.MassCenter].BoxedValue; - ScaledSteps = (bool)ms_entries[(int)ModSetting.ScaledSteps].BoxedValue; - CollisionScale = (bool)ms_entries[(int)ModSetting.CollisionScale].BoxedValue; - OverrideFix = (bool)ms_entries[(int)ModSetting.OverrideFix].BoxedValue; - } - static void OnSliderUpdate(string p_name, string p_value) { if(Enum.TryParse(p_name, out ModSetting l_setting)) @@ -225,6 +226,13 @@ namespace ml_amt } break; + case ModSetting.ScaledJump: + { + ScaledJump = bool.Parse(p_value); + ScaledJumpChange?.Invoke(ScaledJump); + } + break; + case ModSetting.CollisionScale: { CollisionScale = bool.Parse(p_value); diff --git a/ml_amt/Utils.cs b/ml_amt/Utils.cs index f49bc9f..2117d9b 100644 --- a/ml_amt/Utils.cs +++ b/ml_amt/Utils.cs @@ -1,27 +1,58 @@ -using UnityEngine; -using System.Reflection; +using ABI.CCK.Components; using ABI_RC.Core.UI; +using ABI_RC.Systems.MovementSystem; +using RootMotion.FinalIK; +using System.Reflection; +using UnityEngine; namespace ml_amt { static class Utils { - static MethodInfo ms_getSineKeyframes = typeof(RootMotion.FinalIK.IKSolverVR).GetMethod("GetSineKeyframes", BindingFlags.NonPublic | BindingFlags.Static); + static readonly FieldInfo ms_grounded = typeof(MovementSystem).GetField("_isGrounded", BindingFlags.NonPublic | BindingFlags.Instance); + static readonly FieldInfo ms_groundedRaw = typeof(MovementSystem).GetField("_isGroundedRaw", BindingFlags.NonPublic | BindingFlags.Instance); + static readonly FieldInfo ms_hasToes = typeof(IKSolverVR).GetField("hasToes", BindingFlags.NonPublic | BindingFlags.Instance); + static MethodInfo ms_getSineKeyframes = typeof(IKSolverVR).GetMethod("GetSineKeyframes", BindingFlags.NonPublic | BindingFlags.Static); static FieldInfo ms_cohtmlView = typeof(CohtmlControlledViewDisposable).GetField("_view", BindingFlags.NonPublic | BindingFlags.Instance); public static bool IsInVR() => ((ABI_RC.Core.Savior.CheckVR.Instance != null) && ABI_RC.Core.Savior.CheckVR.Instance.hasVrDeviceLoaded); - // Extensions - public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false) - { - return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.localScale : Vector3.one); - } + public static bool IsGrounded(this MovementSystem p_instance) => (bool)ms_grounded.GetValue(MovementSystem.Instance); + public static bool IsGroundedRaw(this MovementSystem p_instance) => (bool)ms_groundedRaw.GetValue(MovementSystem.Instance); + public static bool HasToes(this IKSolverVR p_instance) => (bool)ms_hasToes.GetValue(p_instance); public static Keyframe[] GetSineKeyframes(float p_mag) { return (Keyframe[])ms_getSineKeyframes.Invoke(null, new object[] { p_mag }); } - public static void ExecuteScript(this CohtmlControlledViewDisposable p_viewDisposable, string p_script) => ((cohtml.Net.View)ms_cohtmlView.GetValue(p_viewDisposable))?.ExecuteScript(p_script); + public static bool IsWorldSafe() => ((CVRWorld.Instance != null) && CVRWorld.Instance.allowFlying); + public static float GetWorldJumpHeight() + { + float l_result = 1f; + if(CVRWorld.Instance != null) + l_result = CVRWorld.Instance.jumpHeight; + return l_result; + } + public static float GetWorldMovementLimit() + { + float l_result = 1f; + if(CVRWorld.Instance != null) + { + l_result = CVRWorld.Instance.baseMovementSpeed; + l_result *= CVRWorld.Instance.sprintMultiplier; + l_result *= CVRWorld.Instance.inAirMovementMultiplier; + l_result *= CVRWorld.Instance.flyMultiplier; + } + return l_result; + } + + public static void ExecuteScript(this CohtmlControlledViewDisposable p_instance, string p_script) => ((cohtml.Net.View)ms_cohtmlView.GetValue(p_instance))?.ExecuteScript(p_script); + + // Engine extensions + public static Matrix4x4 GetMatrix(this Transform p_transform, bool p_pos = true, bool p_rot = true, bool p_scl = false) + { + return Matrix4x4.TRS(p_pos ? p_transform.position : Vector3.zero, p_rot ? p_transform.rotation : Quaternion.identity, p_scl ? p_transform.localScale : Vector3.one); + } } } diff --git a/ml_amt/ml_amt.csproj b/ml_amt/ml_amt.csproj index d3318ed..eb0ac10 100644 --- a/ml_amt/ml_amt.csproj +++ b/ml_amt/ml_amt.csproj @@ -6,7 +6,7 @@ None AvatarMotionTweaker AvatarMotionTweaker - 1.2.4 + 1.2.8 x64 ml_amt @@ -33,8 +33,7 @@ - D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\0Harmony.dll - false + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\0Harmony.dll D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll @@ -53,9 +52,13 @@ false - D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\MelonLoader.dll - false - false + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll + + + D:\games\Steam\steamapps\common\ChilloutVR\Mods\ml_pmc.dll + + + D:\games\Steam\steamapps\common\ChilloutVR\Mods\ml_prm.dll D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.dll diff --git a/ml_amt/resources/menu.js b/ml_amt/resources/menu.js index 152a75c..e8fb5b3 100644 --- a/ml_amt/resources/menu.js +++ b/ml_amt/resources/menu.js @@ -263,16 +263,25 @@ function inp_toggle_mod_amt(_obj, _callbackName) {
+ +

Avatar independent game fixes/overhauls


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