From bc6e2894956e7dee9b878098738bd2da40b24010 Mon Sep 17 00:00:00 2001 From: SDraw Date: Sat, 8 Apr 2023 02:51:46 +0300 Subject: [PATCH] New mod - PlayerRagdollMod Update to MelonLoader 0.6.1 --- README.md | 1 + ml_amt/ml_amt.csproj.user | 2 +- ml_dht/ml_dht.csproj.user | 2 +- ml_drs/ml_drs.csproj | 8 +- ml_drs/ml_drs.csproj.user | 2 +- ml_egn/ml_egn.csproj.user | 2 +- ml_lme/ml_lme.csproj.user | 2 +- ml_mods_cvr.sln | 6 + ml_pam/ml_pam.csproj.user | 2 +- ml_prm/Main.cs | 98 ++++++++++++ ml_prm/Properties/AssemblyInfo.cs | 12 ++ ml_prm/README.md | 21 +++ ml_prm/RagdollController.cs | 246 +++++++++++++++++++++++++++++ ml_prm/Settings.cs | 79 +++++++++ ml_prm/Utils.cs | 18 +++ ml_prm/ml_prm.csproj | 140 ++++++++++++++++ ml_prm/ml_prm.csproj.user | 6 + ml_prm/vendor/RootMotion/ReadMe.md | 2 + 18 files changed, 640 insertions(+), 9 deletions(-) create mode 100644 ml_prm/Main.cs create mode 100644 ml_prm/Properties/AssemblyInfo.cs create mode 100644 ml_prm/README.md create mode 100644 ml_prm/RagdollController.cs create mode 100644 ml_prm/Settings.cs create mode 100644 ml_prm/Utils.cs create mode 100644 ml_prm/ml_prm.csproj create mode 100644 ml_prm/ml_prm.csproj.user create mode 100644 ml_prm/vendor/RootMotion/ReadMe.md diff --git a/README.md b/README.md index 5b3f57d..230bde6 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,5 @@ Merged set of MelonLoader mods for ChilloutVR. | Four Point Tracking | ml_fpt | 1.0.9 | Retired | Deprecated | In-game feature since 2022r170 update | Leap Motion Extension | ml_lme | 1.3.3 | Yes, update review | Working | | Pickup Arm Movement | ml_pam | 1.0.2 | Yes, update review| Working | +| Player Ragdoll Mod | ml_prm | 1.0.0 | Yes, publish review | Working | | Server Connection Info | ml_sci | 1.0.2 | Retired | Retired | Superseded by `Extended Game Notifications` diff --git a/ml_amt/ml_amt.csproj.user b/ml_amt/ml_amt.csproj.user index 04df561..d2e6c0a 100644 --- a/ml_amt/ml_amt.csproj.user +++ b/ml_amt/ml_amt.csproj.user @@ -1,6 +1,6 @@  - D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\;D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\;D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ \ No newline at end of file diff --git a/ml_dht/ml_dht.csproj.user b/ml_dht/ml_dht.csproj.user index 04df561..d2e6c0a 100644 --- a/ml_dht/ml_dht.csproj.user +++ b/ml_dht/ml_dht.csproj.user @@ -1,6 +1,6 @@  - D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\;D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\;D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ \ No newline at end of file diff --git a/ml_drs/ml_drs.csproj b/ml_drs/ml_drs.csproj index 41b71ab..3809559 100644 --- a/ml_drs/ml_drs.csproj +++ b/ml_drs/ml_drs.csproj @@ -35,6 +35,7 @@ False False + D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll @@ -44,8 +45,9 @@ C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll False - - C:\Games\Steam\common\ChilloutVR\MelonLoader\MelonLoader.dll + + False + D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\MelonLoader.dll False @@ -69,6 +71,6 @@ - copy /y "$(TargetPath)" "C:\Games\Steam\common\ChilloutVR\Mods\" + copy /y "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\" \ No newline at end of file diff --git a/ml_drs/ml_drs.csproj.user b/ml_drs/ml_drs.csproj.user index 2539084..5c67b34 100644 --- a/ml_drs/ml_drs.csproj.user +++ b/ml_drs/ml_drs.csproj.user @@ -1,6 +1,6 @@  - C:\Games\Steam\common\ChilloutVR\MelonLoader\;C:\Games\Steam\common\ChilloutVR\ChilloutVR_Data\Managed\ + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\;D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ \ No newline at end of file diff --git a/ml_egn/ml_egn.csproj.user b/ml_egn/ml_egn.csproj.user index 04df561..d2e6c0a 100644 --- a/ml_egn/ml_egn.csproj.user +++ b/ml_egn/ml_egn.csproj.user @@ -1,6 +1,6 @@  - D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\;D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\;D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ \ No newline at end of file diff --git a/ml_lme/ml_lme.csproj.user b/ml_lme/ml_lme.csproj.user index 04df561..d2e6c0a 100644 --- a/ml_lme/ml_lme.csproj.user +++ b/ml_lme/ml_lme.csproj.user @@ -1,6 +1,6 @@  - D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\;D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\;D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ \ No newline at end of file diff --git a/ml_mods_cvr.sln b/ml_mods_cvr.sln index d9193c5..19f4b60 100644 --- a/ml_mods_cvr.sln +++ b/ml_mods_cvr.sln @@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ml_egn", "ml_egn\ml_egn.csp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ml_pam", "ml_pam\ml_pam.csproj", "{3B5028DE-8C79-40DF-A1EF-BDB29D366125}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ml_prm", "ml_prm\ml_prm.csproj", "{ABD2A720-2DE8-4EB3-BFC2-8F1C3D2ADA15}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -45,6 +47,10 @@ Global {3B5028DE-8C79-40DF-A1EF-BDB29D366125}.Debug|x64.Build.0 = Debug|x64 {3B5028DE-8C79-40DF-A1EF-BDB29D366125}.Release|x64.ActiveCfg = Release|x64 {3B5028DE-8C79-40DF-A1EF-BDB29D366125}.Release|x64.Build.0 = Release|x64 + {ABD2A720-2DE8-4EB3-BFC2-8F1C3D2ADA15}.Debug|x64.ActiveCfg = Debug|x64 + {ABD2A720-2DE8-4EB3-BFC2-8F1C3D2ADA15}.Debug|x64.Build.0 = Debug|x64 + {ABD2A720-2DE8-4EB3-BFC2-8F1C3D2ADA15}.Release|x64.ActiveCfg = Release|x64 + {ABD2A720-2DE8-4EB3-BFC2-8F1C3D2ADA15}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ml_pam/ml_pam.csproj.user b/ml_pam/ml_pam.csproj.user index 04df561..d2e6c0a 100644 --- a/ml_pam/ml_pam.csproj.user +++ b/ml_pam/ml_pam.csproj.user @@ -1,6 +1,6 @@  - D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\;D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\;D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\ \ No newline at end of file diff --git a/ml_prm/Main.cs b/ml_prm/Main.cs new file mode 100644 index 0000000..806d6e2 --- /dev/null +++ b/ml_prm/Main.cs @@ -0,0 +1,98 @@ +using ABI_RC.Core.Player; +using System; +using System.Reflection; +using ABI_RC.Core.InteractionSystem; +using UnityEngine; + +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(); + + 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(CVRSeat).GetMethod(nameof(CVRSeat.SitDown)), + new HarmonyLib.HarmonyMethod(typeof(PlayerRagdollMod).GetMethod(nameof(OnCVRSeatSitDown_Prefix), BindingFlags.Static | BindingFlags.NonPublic)), + null + ); + + MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer()); + } + + public override void OnDeinitializeMelon() + { + if(ms_instance == this) + ms_instance = null; + } + + System.Collections.IEnumerator WaitForLocalPlayer() + { + while(PlayerSetup.Instance == null) + yield return null; + + m_localController = PlayerSetup.Instance.gameObject.AddComponent(); + } + + 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 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); + } + } + + } +} diff --git a/ml_prm/Properties/AssemblyInfo.cs b/ml_prm/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9e478a5 --- /dev/null +++ b/ml_prm/Properties/AssemblyInfo.cs @@ -0,0 +1,12 @@ +using System.Reflection; + +[assembly: AssemblyTitle("PlayerRagdollMod")] +[assembly: AssemblyVersion("1.0.0")] +[assembly: AssemblyFileVersion("1.0.0")] + +[assembly: MelonLoader.MelonInfo(typeof(ml_prm.PlayerRagdollMod), "PlayerRagdollMod", "1.0.0", "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/README.md b/ml_prm/README.md new file mode 100644 index 0000000..9958531 --- /dev/null +++ b/ml_prm/README.md @@ -0,0 +1,21 @@ +# Player Ragdoll Mod +This mod turns your player's avatar into ragdoll puppet. + +# Installation +* Install [latest MelonLoader](https://github.com/LavaGang/MelonLoader) +* Get [latest release DLL](../../../releases/latest): + * Put `ml_prm.dll` in `Mods` folder of game + +# Usage +* Press `R` to turn into ragdoll and back. + +Optional mod's settings with [BTKUILib](https://github.com/BTK-Development/BTKUILib): +* **Switch ragdoll:** turns into ragdoll state and back, made for VR usage primarily. +* **Use hotkey:** enables/disables ragdoll state switch with `R` key; `true` by default. +* **Restore position:** returns to position of ragdoll state activation upon ragdoll state exit; `false` by default. +* **Velocity multiplier:** velocity force multiplier based on player's movement direction; `2.0` by default. + +# Notes +* Incompatible with `Follow hips on IK override` option in AvatarMotionTweaker. +* Even if locally ragdoll state is activated in the middle of playing emote, remote players still see whole emote animation. +* Not suggested to activate fly mode with enabled ragdoll state. diff --git a/ml_prm/RagdollController.cs b/ml_prm/RagdollController.cs new file mode 100644 index 0000000..4615da4 --- /dev/null +++ b/ml_prm/RagdollController.cs @@ -0,0 +1,246 @@ +using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.Player; +using ABI_RC.Systems.MovementSystem; +using RootMotion.Dynamics; +using RootMotion.FinalIK; +using System.Collections.Generic; +using UnityEngine; + +namespace ml_prm +{ + class RagdollController : MonoBehaviour + { + VRIK m_vrIK = null; + float m_vrIkWeight = 1f; + + 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; + BipedRagdollReferences m_avatarReferences; + readonly List> m_boneLinks = null; + + bool m_avatarReady = false; + Vector3 m_lastPosition = Vector3.zero; + Vector3 m_velocity = Vector3.zero; + + internal RagdollController() + { + m_rigidBodies = new List(); + m_colliders = new List(); + m_boneLinks = new List>(); + } + + // Unity events + void Start() + { + m_puppetRoot = new GameObject("[PlayerAvatarPuppet]").transform; + m_puppetRoot.parent = PlayerSetup.Instance.transform; + m_puppetRoot.localPosition = Vector3.zero; + m_puppetRoot.localRotation = Quaternion.identity; + + Settings.SwitchChange += this.SwitchRagdoll; + } + + void OnDestroy() + { + Settings.SwitchChange -= this.SwitchRagdoll; + } + + void Update() + { + Vector3 l_pos = PlayerSetup.Instance.transform.position; + m_velocity = (m_velocity + (l_pos - m_lastPosition) / Time.deltaTime) * 0.5f; + m_lastPosition = l_pos; + + if(Settings.Hotkey && Input.GetKeyDown(KeyCode.R) && !ViewManager.Instance.isGameMenuOpen()) + SwitchRagdoll(); + } + + void LateUpdate() + { + if(m_enabled && m_avatarReady) + { + foreach(var l_link in m_boneLinks) + l_link.Item1.CopyGlobal(l_link.Item2); + } + } + + // Game events + internal void OnAvatarClear() + { + if(m_enabled) + 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_rigidBodies.Clear(); + m_colliders.Clear(); + m_avatarReferences = new BipedRagdollReferences(); + m_puppetReferences = new BipedRagdollReferences(); + m_boneLinks.Clear(); + } + + internal void OnAvatarSetup() + { + if(PlayerSetup.Instance._animator.isHuman) + { + m_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(m_avatarReferences.hips, m_puppetReferences.root, "Hips"); + m_puppetReferences.spine = CloneTransform(m_avatarReferences.spine, m_puppetReferences.hips, "Spine"); + m_puppetReferences.chest = CloneTransform(m_avatarReferences.chest, m_puppetReferences.spine, "Chest"); + m_puppetReferences.head = CloneTransform(m_avatarReferences.head, m_puppetReferences.chest, "Head"); + + m_puppetReferences.leftUpperArm = CloneTransform(m_avatarReferences.leftUpperArm, m_puppetReferences.chest, "LeftUpperArm"); + m_puppetReferences.leftLowerArm = CloneTransform(m_avatarReferences.leftLowerArm, m_puppetReferences.leftUpperArm, "LeftLowerArm"); + m_puppetReferences.leftHand = CloneTransform(m_avatarReferences.leftHand, m_puppetReferences.leftLowerArm, "LeftHand"); + + m_puppetReferences.rightUpperArm = CloneTransform(m_avatarReferences.rightUpperArm, m_puppetReferences.chest, "RightUpperArm"); + m_puppetReferences.rightLowerArm = CloneTransform(m_avatarReferences.rightLowerArm, m_puppetReferences.rightUpperArm, "RightLowerArm"); + m_puppetReferences.rightHand = CloneTransform(m_avatarReferences.rightHand, m_puppetReferences.rightLowerArm, "RightHand"); + + m_puppetReferences.leftUpperLeg = CloneTransform(m_avatarReferences.leftUpperLeg, m_puppetReferences.hips, "LeftUpperLeg"); + m_puppetReferences.leftLowerLeg = CloneTransform(m_avatarReferences.leftLowerLeg, m_puppetReferences.leftUpperLeg, "LeftLowerLeg"); + m_puppetReferences.leftFoot = CloneTransform(m_avatarReferences.leftFoot, m_puppetReferences.leftLowerLeg, "LeftFoot"); + + m_puppetReferences.rightUpperLeg = CloneTransform(m_avatarReferences.rightUpperLeg, m_puppetReferences.hips, "RightUpperLeg"); + m_puppetReferences.rightLowerLeg = CloneTransform(m_avatarReferences.rightLowerLeg, m_puppetReferences.rightUpperLeg, "RightLowerLeg"); + m_puppetReferences.rightFoot = CloneTransform(m_avatarReferences.rightFoot, m_puppetReferences.rightLowerLeg, "RightFoot"); + + 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 = m_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 = 0.5f; + l_body.drag = 1.0f; + l_body.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic; + } + + CharacterJoint l_joint = l_puppetTransforms[i].GetComponent(); + if(l_joint != null) + { + l_joint.enablePreprocessing = false; + l_joint.enableProjection = true; + } + + Collider l_collider = l_puppetTransforms[i].GetComponent(); + if(l_collider != null) + { + Physics.IgnoreCollision(MovementSystem.Instance.proxyCollider, l_collider, true); + l_collider.enabled = false; + m_colliders.Add(l_collider); + } + + if(l_avatarTransforms[i] != null) + m_boneLinks.Add(System.Tuple.Create(l_puppetTransforms[i], l_avatarTransforms[i])); + } + } + + m_vrIK = PlayerSetup.Instance._avatar.GetComponent(); + if(m_vrIK != null) + { + m_vrIK.onPreSolverUpdate.AddListener(this.OnIKPreUpdate); + m_vrIK.onPostSolverUpdate.AddListener(this.OnIKPostUpdate); + } + + m_avatarReady = true; + } + } + + internal void OnSeatSitDown(CVRSeat p_seat) + { + if(m_enabled && m_avatarReady && !p_seat.occupied) + 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; + } + + // Arbitrary + public void SwitchRagdoll() + { + if(m_avatarReady && (MovementSystem.Instance.lastSeat == null)) + { + m_enabled = !m_enabled; + + MovementSystem.Instance.SetImmobilized(m_enabled); + + if(m_enabled) + { + foreach(var l_link in m_boneLinks) + l_link.Item2.CopyGlobal(l_link.Item1); + + foreach(Rigidbody l_body in m_rigidBodies) + l_body.isKinematic = false; + + Vector3 l_velocity = m_velocity * Settings.Multiplier; + foreach(Rigidbody l_body in m_rigidBodies) + { + l_body.velocity = l_velocity; + l_body.angularVelocity = Vector3.zero; + } + } + else + { + foreach(Rigidbody l_body in m_rigidBodies) + l_body.isKinematic = true; + + if(!Settings.RestorePosition && (m_puppetReferences.hips != null)) + { + Vector3 l_pos = m_puppetReferences.hips.position; + PlayerSetup.Instance.transform.position = l_pos; + } + } + + foreach(Collider l_collider in m_colliders) + l_collider.enabled = 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; + } + } +} diff --git a/ml_prm/Settings.cs b/ml_prm/Settings.cs new file mode 100644 index 0000000..ea68fa3 --- /dev/null +++ b/ml_prm/Settings.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ml_prm +{ + static class Settings + { + public enum ModSetting + { + Hotkey = 0, + Multiplier, + RestorePosition + } + + public static bool Hotkey { get; private set; } = true; + public static float Multiplier { get; private set; } = 2f; + public static bool RestorePosition { get; private set; } = false; + + static public event Action SwitchChange; + static public event Action HotkeyChange; + static public event Action RestorePositionChange; + static public event Action MultiplierChange; + + static MelonLoader.MelonPreferences_Category ms_category = null; + static List ms_entries = null; + + internal static void Init() + { + ms_category = MelonLoader.MelonPreferences.CreateCategory("PRM"); + ms_entries = new List() + { + ms_category.CreateEntry(ModSetting.Hotkey.ToString(), Hotkey), + ms_category.CreateEntry(ModSetting.Multiplier.ToString(), Multiplier), + ms_category.CreateEntry(ModSetting.RestorePosition.ToString(), RestorePosition), + }; + + Hotkey = (bool)ms_entries[(int)ModSetting.Hotkey].BoxedValue; + Multiplier = (float)ms_entries[(int)ModSetting.Multiplier].BoxedValue; + RestorePosition = (bool)ms_entries[(int)ModSetting.RestorePosition].BoxedValue; + + if(MelonLoader.MelonMod.RegisteredMelons.First(m => m.Info.Name == "BTKUILib") != null) + { + CreateBtkUi(); + } + } + + static void CreateBtkUi() + { + var l_categoryMain = BTKUILib.QuickMenuAPI.MiscTabPage.AddCategory("PlayerRagdollMod"); + var l_page = l_categoryMain.AddPage("Player Ragdoll Settings", "", "PlayerRagdollMod settings", "PlayerRagdollMod"); + l_page.MenuTitle = "Ragdoll settings"; + var l_categoryMod = l_page.AddCategory("Settings"); + + l_categoryMod.AddButton("Switch ragdoll", "", "Switch between normal and ragdoll state").OnPress += () => + { + SwitchChange?.Invoke(); + }; + l_categoryMod.AddToggle("Use hotkey", "Switch ragdoll mode with 'R' key", Hotkey).OnValueUpdated += (state) => + { + Hotkey = state; + ms_entries[(int)ModSetting.Hotkey].BoxedValue = state; + HotkeyChange?.Invoke(Hotkey); + }; + l_categoryMod.AddToggle("Restore position", "Bring avatar back where ragdoll state was activated", RestorePosition).OnValueUpdated += (state) => + { + RestorePosition = state; + ms_entries[(int)ModSetting.RestorePosition].BoxedValue = state; + RestorePositionChange?.Invoke(Hotkey); + }; + l_page.AddSlider("Velocity multiplier", "Velocity multiplier upon entering ragdoll state", Multiplier, 1f, 50f).OnValueUpdated += (value) => + { + Multiplier = value; + ms_entries[(int)ModSetting.Multiplier].BoxedValue = value; + MultiplierChange?.Invoke(value); + }; + } + } +} diff --git a/ml_prm/Utils.cs b/ml_prm/Utils.cs new file mode 100644 index 0000000..76fa779 --- /dev/null +++ b/ml_prm/Utils.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace ml_prm +{ + static class Utils + { + 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..8c2917c --- /dev/null +++ b/ml_prm/ml_prm.csproj @@ -0,0 +1,140 @@ + + + + + Debug + AnyCPU + {ABD2A720-2DE8-4EB3-BFC2-8F1C3D2ADA15} + Library + Properties + ml_prm + ml_prm + 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 + D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\0Harmony.dll + False + + + False + False + + + False + False + + + D:\games\Steam\steamapps\common\ChilloutVR\Mods\BTKUILib.dll + False + + + False + D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\MelonLoader.dll + False + + + + + + + + + + False + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll + False + + + False + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.ClothModule.dll + False + + + False + False + + + False + False + + + False + D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.PhysicsModule.dll + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + copy /y "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\" + + \ No newline at end of file diff --git a/ml_prm/ml_prm.csproj.user b/ml_prm/ml_prm.csproj.user new file mode 100644 index 0000000..4d08fe9 --- /dev/null +++ b/ml_prm/ml_prm.csproj.user @@ -0,0 +1,6 @@ + + + + D:\Games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\;D:\Games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\;D:\Games\Steam\steamapps\common\ChilloutVR\Mods\ + + \ No newline at end of file diff --git a/ml_prm/vendor/RootMotion/ReadMe.md b/ml_prm/vendor/RootMotion/ReadMe.md new file mode 100644 index 0000000..5455e7a --- /dev/null +++ b/ml_prm/vendor/RootMotion/ReadMe.md @@ -0,0 +1,2 @@ +* Buy https://assetstore.unity.com/packages/tools/physics/puppetmaster-48977 +* Put `PuppetMaster` and `RagdollManager` scripts in this folder