From 22391d1fcc597e3d8ddb7d29aa1cb6a749db95c0 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidOnSteam@users.noreply.github.com> Date: Sun, 25 Dec 2022 22:27:12 -0600 Subject: [PATCH] final touches completely migrated to new setup using a helper monobehavior, which makes code a lot cleaner --- DesktopVRIK/DesktopVRIK.cs | 92 +++++---- DesktopVRIK/HarmonyPatches.cs | 64 ++++-- DesktopVRIK/Main.cs | 262 ++++--------------------- DesktopVRIK/Properties/AssemblyInfo.cs | 2 +- DesktopVRIK/format.json | 24 +-- 5 files changed, 148 insertions(+), 296 deletions(-) diff --git a/DesktopVRIK/DesktopVRIK.cs b/DesktopVRIK/DesktopVRIK.cs index ba4e1dd..096940a 100644 --- a/DesktopVRIK/DesktopVRIK.cs +++ b/DesktopVRIK/DesktopVRIK.cs @@ -1,62 +1,65 @@ using ABI.CCK.Components; using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; -using ABI_RC.Systems.MovementSystem; using ABI_RC.Systems.IK; using ABI_RC.Systems.IK.SubSystems; -using MelonLoader; -using System.Text; -using System.Collections; -using System.Collections.Generic; -using UnityEngine; +using ABI_RC.Systems.MovementSystem; using RootMotion.FinalIK; +using UnityEngine; +using UnityEngine.Events; namespace DesktopVRIK; -public class NAKDesktopVRIK : MonoBehaviour +public class DesktopVRIK : MonoBehaviour { - public static NAKDesktopVRIK Instance; - public VRIK vrik; + public static DesktopVRIK Instance; + + public bool Setting_Enabled; + public bool Setting_EmulateVRChatHipMovement; + public bool Setting_EmoteVRIK; + public bool Setting_EmoteLookAtIK; void Start() { Instance = this; } - void LateUpdate() + public void OnPreSolverUpdate() { - //pretty much zero out VRIK trying to locomote us using autofootstep - transform.localPosition = Vector3.zero; - transform.localRotation = Quaternion.identity; + //Reset avatar offset (VRIK will literally make you walk away from root otherwise) + IKSystem.vrik.transform.localPosition = Vector3.zero; + IKSystem.vrik.transform.localRotation = Quaternion.identity; + + //VRChat hip movement emulation + if (Setting_EmulateVRChatHipMovement) + { + float angle = PlayerSetup.Instance.desktopCamera.transform.localEulerAngles.x; + angle = (angle > 180) ? angle - 360 : angle; + float weight = (1 - MovementSystem.Instance.movementVector.magnitude); + Quaternion rotation = Quaternion.AngleAxis(angle * weight, IKSystem.Instance.avatar.transform.right); + IKSystem.vrik.solver.AddRotationOffset(IKSolverVR.RotationOffset.Head, rotation); + } } public void CalibrateAvatarVRIK(CVRAvatar avatar) { - //check if VRIK already exists, as it is an allowed component - vrik = avatar.gameObject.GetComponent(); - if (vrik == null) - { - vrik = avatar.gameObject.AddComponent(); - } - //Generic VRIK calibration shit - vrik.fixTransforms = false; - vrik.solver.plantFeet = false; - vrik.solver.locomotion.weight = 1f; - vrik.solver.locomotion.angleThreshold = 30f; - vrik.solver.locomotion.maxLegStretch = 0.75f; + IKSystem.vrik.fixTransforms = false; + IKSystem.vrik.solver.plantFeet = false; + IKSystem.vrik.solver.locomotion.weight = 1f; + IKSystem.vrik.solver.locomotion.angleThreshold = 30f; + IKSystem.vrik.solver.locomotion.maxLegStretch = 0.75f; //nuke weights - vrik.solver.spine.headClampWeight = 0f; - vrik.solver.spine.minHeadHeight = 0f; - vrik.solver.leftArm.positionWeight = 0f; - vrik.solver.leftArm.rotationWeight = 0f; - vrik.solver.rightArm.positionWeight = 0f; - vrik.solver.rightArm.rotationWeight = 0f; - vrik.solver.leftLeg.positionWeight = 0f; - vrik.solver.leftLeg.rotationWeight = 0f; - vrik.solver.rightLeg.positionWeight = 0f; - vrik.solver.rightLeg.rotationWeight = 0f; + IKSystem.vrik.solver.spine.headClampWeight = 0f; + IKSystem.vrik.solver.spine.minHeadHeight = 0f; + IKSystem.vrik.solver.leftArm.positionWeight = 0f; + IKSystem.vrik.solver.leftArm.rotationWeight = 0f; + IKSystem.vrik.solver.rightArm.positionWeight = 0f; + IKSystem.vrik.solver.rightArm.rotationWeight = 0f; + IKSystem.vrik.solver.leftLeg.positionWeight = 0f; + IKSystem.vrik.solver.leftLeg.rotationWeight = 0f; + IKSystem.vrik.solver.rightLeg.positionWeight = 0f; + IKSystem.vrik.solver.rightLeg.rotationWeight = 0f; //ChilloutVR specific stuff @@ -69,15 +72,20 @@ public class NAKDesktopVRIK : MonoBehaviour BodySystem.TrackingRightArmEnabled = false; BodySystem.TrackingLeftLegEnabled = false; BodySystem.TrackingRightLegEnabled = false; - vrik.solver.IKPositionWeight = 0f; - vrik.enabled = false; + IKSystem.vrik.solver.IKPositionWeight = 0f; + IKSystem.vrik.enabled = false; //Calibrate HeadIKOffset - VRIKCalibrator.CalibrateHead(vrik, headAnchor, IKSystem.Instance.headAnchorPositionOffset, IKSystem.Instance.headAnchorRotationOffset); - vrik.enabled = true; - vrik.solver.IKPositionWeight = 1f; - vrik.solver.spine.maintainPelvisPosition = 0f; + VRIKCalibrator.CalibrateHead(IKSystem.vrik, headAnchor, IKSystem.Instance.headAnchorPositionOffset, IKSystem.Instance.headAnchorRotationOffset); + IKSystem.vrik.enabled = true; + IKSystem.vrik.solver.IKPositionWeight = 1f; + IKSystem.vrik.solver.spine.maintainPelvisPosition = 0f; + if (IKSystem.vrik != null) + { + IKSystem.vrik.onPreSolverUpdate.AddListener(new UnityAction(this.OnPreSolverUpdate)); + } } + //This is built because original build placed IK Targets on all joints. private static Transform FindIKTarget(Transform targetParent) { /** diff --git a/DesktopVRIK/HarmonyPatches.cs b/DesktopVRIK/HarmonyPatches.cs index 43cc8eb..38cf1ba 100644 --- a/DesktopVRIK/HarmonyPatches.cs +++ b/DesktopVRIK/HarmonyPatches.cs @@ -1,10 +1,9 @@ -using ABI_RC.Core.Player; -using ABI.CCK.Components; +using ABI.CCK.Components; +using ABI_RC.Core.Player; using ABI_RC.Core.Savior; using ABI_RC.Systems.IK; using ABI_RC.Systems.IK.SubSystems; using HarmonyLib; -using MelonLoader; using RootMotion.FinalIK; namespace DesktopVRIK; @@ -14,22 +13,59 @@ internal class HarmonyPatches { [HarmonyPostfix] [HarmonyPatch(typeof(PlayerSetup), "SetupAvatarGeneral")] - private static void InitializeDesktopIKSystem(ref CVRAvatar ____avatarDescriptor) + private static void SetupDesktopIKSystem(ref CVRAvatar ____avatarDescriptor) { - if (MetaPort.Instance.isUsingVr) return; - - //this will stop at the useless isVr return (the function is only ever called by vr anyways...) - IKSystem.Instance.InitializeAvatar(____avatarDescriptor); + if (!MetaPort.Instance.isUsingVr && DesktopVRIK.Instance.Setting_Enabled) + { + //this will stop at the useless isVr return (the function is only ever called by vr anyways...) + IKSystem.Instance.InitializeAvatar(____avatarDescriptor); + } } [HarmonyPostfix] [HarmonyPatch(typeof(IKSystem), "InitializeAvatar")] - private static void InitializeDesktopAvatar(CVRAvatar avatar, ref VRIK ____vrik) + private static void InitializeDesktopAvatarVRIK(CVRAvatar avatar, ref VRIK ____vrik) { - //need IKSystem to see VRIK component for setup - ____vrik = avatar.gameObject.AddComponent(); - //now i add my own VRIK stuff - NAKDesktopVRIK NAKVRIK = avatar.gameObject.AddComponent(); - NAKVRIK.CalibrateAvatarVRIK(avatar); + if (!MetaPort.Instance.isUsingVr && DesktopVRIK.Instance.Setting_Enabled) + { + //need IKSystem to see VRIK component for setup + ____vrik = avatar.gameObject.AddComponent(); + //now I calibrate DesktopVRIK + DesktopVRIK.Instance.CalibrateAvatarVRIK(avatar); + } + } + + private static bool emotePlayed = false; + + [HarmonyPostfix] + [HarmonyPatch(typeof(PlayerSetup), "Update")] + private static void CorrectVRIK(ref bool ____emotePlaying, ref LookAtIK ___lookIK) + { + if (MetaPort.Instance.isUsingVr || DesktopVRIK.Instance == null) return; + + //might need to rework this in the future + if (____emotePlaying && !emotePlayed) + { + emotePlayed = true; + if (DesktopVRIK.Instance.Setting_EmoteVRIK) + { + BodySystem.TrackingEnabled = false; + //IKSystem.vrik.solver.Reset(); + } + if (DesktopVRIK.Instance.Setting_EmoteLookAtIK && ___lookIK != null) + { + ___lookIK.enabled = false; + } + } + else if (!____emotePlaying && emotePlayed) + { + emotePlayed = false; + IKSystem.vrik.solver.Reset(); + BodySystem.TrackingEnabled = true; + if (___lookIK != null) + { + ___lookIK.enabled = true; + } + } } } \ No newline at end of file diff --git a/DesktopVRIK/Main.cs b/DesktopVRIK/Main.cs index 21e25d9..a04b2fc 100644 --- a/DesktopVRIK/Main.cs +++ b/DesktopVRIK/Main.cs @@ -1,240 +1,48 @@ -using ABI.CCK.Components; -using ABI_RC.Core.Player; -using ABI_RC.Core.Savior; -using ABI_RC.Systems.MovementSystem; -using ABI_RC.Systems.IK; -using ABI_RC.Systems.IK.SubSystems; -using HarmonyLib; +using ABI_RC.Core.Player; using MelonLoader; -using RootMotion.FinalIK; -using UnityEngine; -using UnityEngine.Events; namespace DesktopVRIK; -public class DesktopVRIK : MelonMod +public class DesktopVRIKMod : MelonMod { - + internal const string SettingsCategory = "DesktopVRIK"; private static MelonPreferences_Category m_categoryDesktopVRIK; - private static MelonPreferences_Entry m_entryEnabled; - private static MelonPreferences_Entry m_entryEmulateHipMovement; - private static MelonPreferences_Entry m_entryEmoteVRIK; - private static MelonPreferences_Entry m_entryEmoteLookAtIK; - - public override void OnApplicationStart() + private static MelonPreferences_Entry m_entryEnabled, m_entryEmulateHipMovement, m_entryEmoteVRIK, m_entryEmoteLookAtIK; + public override void OnInitializeMelon() { - m_categoryDesktopVRIK = MelonPreferences.CreateCategory(nameof(DesktopVRIK)); - m_entryEnabled = m_categoryDesktopVRIK.CreateEntry("Enabled", true, description: "Attempt to give Desktop VRIK."); + m_categoryDesktopVRIK = MelonPreferences.CreateCategory(SettingsCategory); + m_entryEnabled = m_categoryDesktopVRIK.CreateEntry("Enabled", true, description: "Attempt to give Desktop VRIK on avatar load."); m_entryEmulateHipMovement = m_categoryDesktopVRIK.CreateEntry("Emulate Hip Movement", true, description: "Emulates VRChat-like hip movement when moving head up/down on desktop."); - m_entryEmoteVRIK = m_categoryDesktopVRIK.CreateEntry("Disable Emote VRIK", true, description: "Disable VRIK while emoting."); - m_entryEmoteLookAtIK = m_categoryDesktopVRIK.CreateEntry("Disable Emote LookAtIK", true, description: "Disable LookAtIK while emoting."); + m_entryEmoteVRIK = m_categoryDesktopVRIK.CreateEntry("Disable Emote VRIK", true, description: "Disable VRIK while emoting. Only disable if you are ok with looking dumb."); + m_entryEmoteLookAtIK = m_categoryDesktopVRIK.CreateEntry("Disable Emote LookAtIK", true, description: "Disable LookAtIK while emoting. This setting doesn't really matter, as LookAtIK isn't networked while doing an emote."); + m_categoryDesktopVRIK.SaveToFile(false); + + foreach (var setting in m_categoryDesktopVRIK.Entries) + { + setting.OnEntryValueChangedUntyped.Subscribe(OnUpdateSettings); + } + + MelonLoader.MelonCoroutines.Start(WaitForLocalPlayer()); } - [HarmonyPatch] - private class HarmonyPatches + System.Collections.IEnumerator WaitForLocalPlayer() { - private static bool emotePlayed = false; - - [HarmonyPostfix] - [HarmonyPatch(typeof(PlayerSetup), "Update")] - private static void CorrectVRIK(ref bool ____emotePlaying, ref LookAtIK ___lookIK) - { - if (MetaPort.Instance.isUsingVr) return; - - if (IKSystem.vrik == null) return; - - //pretty much zero out VRIK trying to locomote us using autofootstep - IKSystem.Instance.avatar.transform.localPosition = Vector3.zero; - IKSystem.Instance.avatar.transform.localRotation = Quaternion.identity; - - //TODO: Smooth out offset when walking/running - if ( m_entryEmulateHipMovement.Value ) - { - float angle = PlayerSetup.Instance.desktopCamera.transform.localEulerAngles.x; - angle = (angle > 180) ? angle - 360 : angle; - float weight = (1 - MovementSystem.Instance.movementVector.magnitude); - Quaternion rotation = Quaternion.AngleAxis(angle * weight, IKSystem.Instance.avatar.transform.right); - IKSystem.vrik.solver.AddRotationOffset(IKSolverVR.RotationOffset.Head, rotation); - } - - - //Avatar Motion Tweaker has custom emote detection to disable VRIK via state tags - if (____emotePlaying && !emotePlayed) - { - emotePlayed = true; - if (m_entryEmoteVRIK.Value) - { - BodySystem.TrackingEnabled = false; - IKSystem.vrik.solver.Reset(); - } - if (m_entryEmoteLookAtIK.Value && ___lookIK != null) - { - ___lookIK.enabled = false; - } - } - else if (!____emotePlaying && emotePlayed) - { - emotePlayed = false; - BodySystem.TrackingEnabled = true; - IKSystem.vrik.solver.Reset(); - if (___lookIK != null) - { - ___lookIK.enabled = true; - } - } - } - - [HarmonyPostfix] - [HarmonyPatch(typeof(IKSystem), "InitializeAvatar")] - private static void InitializeDesktopAvatar(CVRAvatar avatar, ref VRIK ____vrik, ref HumanPoseHandler ____poseHandler, ref Vector3 ____referenceRootPosition, ref Quaternion ____referenceRootRotation, ref float[] ___HandCalibrationPoseMuscles) - { - if (!m_entryEnabled.Value) return; - - if (MetaPort.Instance.isUsingVr) return; - - //set avatar to - Quaternion initialRotation = avatar.transform.rotation; - avatar.transform.rotation = Quaternion.identity; - - ____vrik = avatar.gameObject.AddComponent(); - ____vrik.fixTransforms = false; - ____vrik.solver.locomotion.weight = 1f; - IKSystem.Instance.ApplyAvatarScaleToIk(avatar.viewPosition.y); - ____vrik.solver.locomotion.angleThreshold = 30f; - ____vrik.solver.locomotion.maxLegStretch = 0.75f; - ____vrik.solver.spine.headClampWeight = 0f; - ____vrik.solver.spine.minHeadHeight = 0f; - if (____vrik != null) - { - ____vrik.onPreSolverUpdate.AddListener(new UnityAction(IKSystem.Instance.OnPreSolverUpdate)); - } - - if (____poseHandler == null) - { - ____poseHandler = new HumanPoseHandler(IKSystem.Instance.animator.avatar, IKSystem.Instance.animator.transform); - } - ____poseHandler.GetHumanPose(ref IKSystem.Instance.humanPose); - ____referenceRootPosition = IKSystem.Instance.animator.GetBoneTransform(HumanBodyBones.Hips).position; - ____referenceRootRotation = IKSystem.Instance.animator.GetBoneTransform(HumanBodyBones.Hips).rotation; - for (int i = 0; i < ___HandCalibrationPoseMuscles.Length; i++) - { - IKSystem.Instance.ApplyMuscleValue((MuscleIndex)i, ___HandCalibrationPoseMuscles[i], ref IKSystem.Instance.humanPose.muscles); - } - ____poseHandler.SetHumanPose(ref IKSystem.Instance.humanPose); - if (IKSystem.Instance.applyOriginalHipPosition) - { - IKSystem.Instance.animator.GetBoneTransform(HumanBodyBones.Hips).position = ____referenceRootPosition; - } - if (IKSystem.Instance.applyOriginalHipRotation) - { - IKSystem.Instance.animator.GetBoneTransform(HumanBodyBones.Hips).rotation = ____referenceRootRotation; - } - - BodySystem.isCalibratedAsFullBody = false; - BodySystem.isCalibrating = false; - BodySystem.TrackingPositionWeight = 1f; - BodySystem.isCalibratedAsFullBody = false; - - //InitializeDesktopIK - - //centerEyeAnchor now is head bone - Transform headAnchor = FindIKTarget(IKSystem.Instance.animator.GetBoneTransform(HumanBodyBones.Head)); - IKSystem.Instance.headAnchorPositionOffset = Vector3.zero; - IKSystem.Instance.headAnchorRotationOffset = Vector3.zero; - - IKSystem.Instance.leftHandModel.SetActive(false); - IKSystem.Instance.rightHandModel.SetActive(false); - IKSystem.Instance.animator = IKSystem.Instance.avatar.GetComponent(); - IKSystem.Instance.animator.cullingMode = AnimatorCullingMode.AlwaysAnimate; - - //tell game to not track limbs - BodySystem.TrackingLeftArmEnabled = false; - BodySystem.TrackingRightArmEnabled = false; - BodySystem.TrackingLeftLegEnabled = false; - BodySystem.TrackingRightLegEnabled = false; - - //create ik targets for avatars to utilize - //____vrik.solver.leftLeg.target = FindIKTarget(IKSystem.Instance.animator.GetBoneTransform(HumanBodyBones.LeftFoot)); - //____vrik.solver.rightLeg.target = FindIKTarget(IKSystem.Instance.animator.GetBoneTransform(HumanBodyBones.RightFoot)); - - ____vrik.solver.IKPositionWeight = 0f; - ____vrik.enabled = false; - - VRIKCalibrator.CalibrateHead(____vrik, headAnchor, IKSystem.Instance.headAnchorPositionOffset, IKSystem.Instance.headAnchorRotationOffset); - //IKSystem.Instance.leftHandPose.transform.position = ____vrik.references.leftHand.position; - //IKSystem.Instance.rightHandPose.transform.position = ____vrik.references.rightHand.position; - //____vrik.solver.leftArm.target = IKSystem.Instance.leftHandAnchor; - //____vrik.solver.rightArm.target = IKSystem.Instance.rightHandAnchor; - - //Transform boneTransform = IKSystem.Instance.animator.GetBoneTransform(HumanBodyBones.LeftLowerArm); - //Transform boneTransform2 = IKSystem.Instance.animator.GetBoneTransform(HumanBodyBones.RightLowerArm); - //if (boneTransform.GetComponent() == null) - //{ - // TwistRelaxer twistRelaxer = boneTransform.gameObject.AddComponent(); - // twistRelaxer.ik = ____vrik; - // twistRelaxer.child = IKSystem.Instance.animator.GetBoneTransform(HumanBodyBones.LeftHand); - // twistRelaxer.weight = 0.5f; - // twistRelaxer.parentChildCrossfade = 0.9f; - // twistRelaxer.twistAngleOffset = 0f; - //} - //if (boneTransform2.GetComponent() == null) - //{ - // TwistRelaxer twistRelaxer2 = boneTransform2.gameObject.AddComponent(); - // twistRelaxer2.ik = ____vrik; - // twistRelaxer2.child = IKSystem.Instance.animator.GetBoneTransform(HumanBodyBones.RightHand); - // twistRelaxer2.weight = 0.5f; - // twistRelaxer2.parentChildCrossfade = 0.9f; - // twistRelaxer2.twistAngleOffset = 0f; - //} - //if (IKSystem.Instance.animator != null && IKSystem.Instance.animator.avatar != null && IKSystem.Instance.animator.avatar.isHuman) - //{ - // Transform boneTransform3 = IKSystem.Instance.animator.GetBoneTransform(HumanBodyBones.LeftThumbProximal); - // if (boneTransform3 != null) - // { - // ____vrik.solver.leftArm.palmToThumbAxis = VRIKCalibrator.GuessPalmToThumbAxis(____vrik.references.leftHand, ____vrik.references.leftForearm, boneTransform3); - // } - // Transform boneTransform4 = IKSystem.Instance.animator.GetBoneTransform(HumanBodyBones.RightThumbProximal); - // if (boneTransform4 != null) - // { - // ____vrik.solver.rightArm.palmToThumbAxis = VRIKCalibrator.GuessPalmToThumbAxis(____vrik.references.rightHand, ____vrik.references.rightForearm, boneTransform4); - // } - //} - - ____vrik.enabled = true; - ____vrik.solver.IKPositionWeight = 1f; - ____vrik.solver.spine.maintainPelvisPosition = 0f; - - //prevent the IK from walking away - ____vrik.solver.locomotion.maxVelocity = 0f; - avatar.transform.rotation = initialRotation; - } - - private static Transform FindIKTarget(Transform targetParent) - { - /** - I want creators to be able to specify their own custom IK Targets, so they can move them around with animations if they want. - We check for existing target objects, and if none are found we make our own. - Naming scheme is parentobject name + " IK Target". - **/ - Transform parentTransform = targetParent.transform; - string targetName = parentTransform.name + " IK Target"; - - //check for existing target - foreach (object obj in parentTransform) - { - Transform childTransform = (Transform)obj; - if (childTransform.name == targetName) - { - return childTransform; - } - } - - //create new target if none are found - GameObject newTarget = new GameObject(targetName); - newTarget.transform.parent = parentTransform; - newTarget.transform.localPosition = Vector3.zero; - newTarget.transform.localRotation = Quaternion.identity; - return newTarget.transform; - } + while (PlayerSetup.Instance == null) + yield return null; + PlayerSetup.Instance.gameObject.AddComponent(); + while (DesktopVRIK.Instance == null) + yield return null; + UpdateAllSettings(); } + + private void UpdateAllSettings() + { + if (!DesktopVRIK.Instance) return; + DesktopVRIK.Instance.Setting_Enabled = m_entryEnabled.Value; + DesktopVRIK.Instance.Setting_EmulateVRChatHipMovement = m_entryEmulateHipMovement.Value; + DesktopVRIK.Instance.Setting_EmoteVRIK = m_entryEmoteVRIK.Value; + DesktopVRIK.Instance.Setting_EmoteLookAtIK = m_entryEmoteLookAtIK.Value; + } + + private void OnUpdateSettings(object arg1, object arg2) => UpdateAllSettings(); } \ No newline at end of file diff --git a/DesktopVRIK/Properties/AssemblyInfo.cs b/DesktopVRIK/Properties/AssemblyInfo.cs index e0f699c..fc089f3 100644 --- a/DesktopVRIK/Properties/AssemblyInfo.cs +++ b/DesktopVRIK/Properties/AssemblyInfo.cs @@ -11,7 +11,7 @@ using DesktopVRIK.Properties; [assembly: AssemblyProduct(nameof(DesktopVRIK))] [assembly: MelonInfo( - typeof(DesktopVRIK.DesktopVRIK), + typeof(DesktopVRIK.DesktopVRIKMod), nameof(DesktopVRIK), AssemblyInfoParams.Version, AssemblyInfoParams.Author, diff --git a/DesktopVRIK/format.json b/DesktopVRIK/format.json index aee1a24..6eef4bf 100644 --- a/DesktopVRIK/format.json +++ b/DesktopVRIK/format.json @@ -1,23 +1,23 @@ { - "_id": 95, + "_id": -1, "name": "DesktopVRIK", - "modversion": "1.1.0", - "gameversion": "2022r168", - "loaderversion": "0.5.4", + "modversion": "1.0.0", + "gameversion": "2022r170", + "loaderversion": "0.5.7", "modtype": "Mod", "author": "NotAKidoS", - "description": "Corrects MM and QM position when avatar is scaled.\nAdditional option to scale player collision.", + "description": "Adds VRIK to Desktop avatars. No longer will you be a liveless sliding statue~!\nAdds the small feet stepping when looking around on Desktop.\n\nAdditional option to emulate VRChat-like hip movement if you like becoming a fish.", "searchtags": [ - "menu", - "scale", - "avatarscale", - "slider" + "desktop", + "vrik", + "ik", + "feet" ], "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidOnSteam/DesktopVRIK/releases/download/r2/DesktopVRIK.dll", + "downloadlink": "https://github.com/NotAKidOnSteam/DesktopVRIK/releases/download/r1/DesktopVRIK.dll", "sourcelink": "https://github.com/NotAKidOnSteam/DesktopVRIK/", - "changelog": "Added option to scale player collision. Fixed some VR specific issues.", - "embedcolor": "804221" + "changelog": "Simplified VRIK calibration to avoid low heel issue.", + "embedcolor": "9b59b6" } \ No newline at end of file