using ABI_RC.Core.InteractionSystem; using ABI_RC.Core.Player; using ABI_RC.Core.Savior; using ABI_RC.Core.UI; using UnityEngine; namespace ml_fpt { public class FourPointTracking : MelonLoader.MelonMod { static FourPointTracking ms_instance = null; bool m_ready = false; IndexIK m_indexIk = null; CVR_IK_Calibrator m_ikCalibrator = null; RootMotion.FinalIK.VRIK m_vrIK = null; RuntimeAnimatorController m_runtimeAnimator = null; bool m_calibrationActive = false; object m_calibrationTask = null; int m_hipsTrackerIndex = -1; Transform m_hips = null; public override void OnApplicationStart() { ms_instance = this; HarmonyInstance.Patch( typeof(PlayerSetup).GetMethod(nameof(PlayerSetup.ClearAvatar)), null, new HarmonyLib.HarmonyMethod(typeof(FourPointTracking).GetMethod(nameof(OnAvatarClear_Postfix), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)) ); 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 System.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_ikCalibrator = PlayerSetup.Instance.gameObject.GetComponent(); m_ready = true; } void StartCalibration() { if(m_ready && !m_calibrationActive && PlayerSetup.Instance._inVr && !PlayerSetup.Instance.avatarIsLoading && PlayerSetup.Instance._animator.isHuman && !m_ikCalibrator.inFullbodyCalibration && !m_ikCalibrator.avatarCalibratedAsFullBody) { 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) { m_hipsTrackerIndex = i; break; } } if(m_hipsTrackerIndex != -1) { m_runtimeAnimator = PlayerSetup.Instance._animator.runtimeAnimatorController; PlayerSetup.Instance._animator.runtimeAnimatorController = 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; m_ikCalibrator.leftHandModel.SetActive(true); m_ikCalibrator.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_ikCalibrator.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; 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; m_ikCalibrator.enabled = true; PlayerSetup.Instance._animator.runtimeAnimatorController = m_runtimeAnimator; m_ikCalibrator.leftHandModel.SetActive(false); m_ikCalibrator.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_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; m_ikCalibrator.enabled = true; m_ikCalibrator.leftHandModel.SetActive(false); m_ikCalibrator.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(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); } } }