using ABI.CCK.Components; using ABI_RC.Core; using ABI_RC.Core.InteractionSystem; using ABI_RC.Core.Player; using ABI_RC.Systems.GameEventSystem; using ABI_RC.Systems.IK; using ABI_RC.Systems.IK.SubSystems; using ABI_RC.Systems.InputManagement; using ABI_RC.Systems.Movement; using RootMotion.Dynamics; using RootMotion.FinalIK; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace ml_prm { [DisallowMultipleComponent] public class RagdollController : MonoBehaviour { const float c_defaultFriction = 0.6f; public static RagdollController Instance { get; private set; } = null; Transform m_avatarTransform = null; Transform m_hips = null; VRIK m_vrIK = null; bool m_applyHipsPosition = false; bool m_applyHipsRotation = false; bool m_avatarReady = false; bool m_ragdolled = false; bool m_forcedSwitch = false; Coroutine m_initTask = null; Transform m_puppet = null; Transform m_puppetRoot = null; BipedRagdollReferences m_puppetReferences; readonly List m_ragdollBodyHandlers = null; readonly List> m_boneLinks = null; readonly List> m_jointAnchors = null; RagdollToggle m_avatarRagdollToggle = null; // Custom component available for editor AvatarParameter m_ragdolledParameter = null; PhysicMaterial m_physicsMaterial = null; bool m_inAir = false; bool m_reachedGround = true; float m_groundedTime = 0f; float m_downTime = float.MinValue; Vector3 m_lastRagdollPosition; Vector3 m_lastSeatPositon; Vector3 m_seatVelocity; Plane m_playerPlane; internal RagdollController() { m_ragdollBodyHandlers = new List(); m_boneLinks = new List>(); m_jointAnchors = new List>(); m_playerPlane = new Plane(); } // Unity events void Awake() { if(Instance != null) { DestroyImmediate(this); return; } Instance = this; DontDestroyOnLoad(this); } void Start() { this.gameObject.layer = CVRLayers.PlayerClone; m_physicsMaterial = new PhysicMaterial("Ragdoll"); m_physicsMaterial.dynamicFriction = c_defaultFriction; m_physicsMaterial.staticFriction = c_defaultFriction; m_physicsMaterial.frictionCombine = PhysicMaterialCombine.Average; m_physicsMaterial.bounciness = 0f; m_physicsMaterial.bounceCombine = PhysicMaterialCombine.Average; m_puppet = new GameObject("[Puppet]").transform; m_puppet.parent = this.transform; m_puppet.localPosition = Vector3.zero; m_puppet.localRotation = Quaternion.identity; Settings.OnMovementDragChanged.AddListener(this.OnMovementDragChanged); Settings.OnAngularDragChanged.AddListener(this.OnAngularDragChanged); Settings.OnGravityChanged.AddListener(this.OnGravityChanged); Settings.OnSlipperinessChanged.AddListener(this.OnPhysicsMaterialChanged); Settings.OnBouncinessChanged.AddListener(this.OnPhysicsMaterialChanged); Settings.OnBuoyancyChanged.AddListener(this.OnBuoyancyChanged); Settings.OnFallDamageChanged.AddListener(this.OnFallDamageChanged); Settings.OnGestureGrabChanged.AddListener(this.OnGestureGrabChanged); CVRGameEventSystem.Avatar.OnLocalAvatarClear.AddListener(this.OnAvatarClear); CVRGameEventSystem.Avatar.OnLocalAvatarLoad.AddListener(this.OnAvatarSetup); GameEvents.OnAvatarPreReuse.AddListener(this.OnAvatarPreReuse); GameEvents.OnAvatarPostReuse.AddListener(this.OnAvatarPostReuse); GameEvents.OnIKScaling.AddListener(this.OnAvatarScaling); GameEvents.OnSeatPreSit.AddListener(this.OnSeatPreSit); GameEvents.OnCalibrationStart.AddListener(this.OnCalibrationStart); GameEvents.OnWorldPreSpawn.AddListener(this.OnWorldPreSpawn); GameEvents.OnCombatPreDown.AddListener(this.OnCombatPreDown); GameEvents.OnFlightChange.AddListener(this.OnFlightChange); GameEvents.OnIKOffsetUpdate.AddListener(this.OnIKOffsetUpdate); BetterBetterCharacterController.OnTeleport.AddListener(this.OnPlayerTeleport); ModUi.OnSwitchChanged.AddListener(this.SwitchRagdoll); } void OnDestroy() { if(Instance == this) Instance = null; if(m_initTask != null) StopCoroutine(m_initTask); m_initTask = null; if(m_puppet != null) Object.Destroy(m_puppet); m_puppet = null; m_puppetRoot = null; m_ragdollBodyHandlers.Clear(); m_boneLinks.Clear(); m_jointAnchors.Clear(); m_avatarRagdollToggle = null; if(m_physicsMaterial != null) Object.Destroy(m_physicsMaterial); m_physicsMaterial = null; Settings.OnMovementDragChanged.RemoveListener(this.OnMovementDragChanged); Settings.OnAngularDragChanged.RemoveListener(this.OnAngularDragChanged); Settings.OnGravityChanged.RemoveListener(this.OnGravityChanged); Settings.OnSlipperinessChanged.RemoveListener(this.OnPhysicsMaterialChanged); Settings.OnBouncinessChanged.RemoveListener(this.OnPhysicsMaterialChanged); Settings.OnBuoyancyChanged.RemoveListener(this.OnBuoyancyChanged); Settings.OnFallDamageChanged.RemoveListener(this.OnFallDamageChanged); Settings.OnGestureGrabChanged.RemoveListener(this.OnGestureGrabChanged); CVRGameEventSystem.Avatar.OnLocalAvatarClear.RemoveListener(this.OnAvatarClear); CVRGameEventSystem.Avatar.OnLocalAvatarLoad.RemoveListener(this.OnAvatarSetup); GameEvents.OnAvatarPreReuse.RemoveListener(this.OnAvatarPreReuse); GameEvents.OnAvatarPostReuse.RemoveListener(this.OnAvatarPostReuse); GameEvents.OnIKScaling.RemoveListener(this.OnAvatarScaling); GameEvents.OnSeatPreSit.RemoveListener(this.OnSeatPreSit); GameEvents.OnCalibrationStart.RemoveListener(this.OnCalibrationStart); GameEvents.OnWorldPreSpawn.RemoveListener(this.OnWorldPreSpawn); GameEvents.OnCombatPreDown.RemoveListener(this.OnCombatPreDown); GameEvents.OnFlightChange.RemoveListener(this.OnFlightChange); GameEvents.OnIKOffsetUpdate.RemoveListener(this.OnIKOffsetUpdate); BetterBetterCharacterController.OnTeleport.RemoveListener(this.OnPlayerTeleport); ModUi.OnSwitchChanged.RemoveListener(this.SwitchRagdoll); } void Update() { if(m_avatarReady) { if(!m_ragdolled && Settings.FallDamage && !BetterBetterCharacterController.Instance.IsFlying() && !BetterBetterCharacterController.Instance.IsSitting()) { bool l_grounded = BetterBetterCharacterController.Instance.IsGrounded(); bool l_inWater = BetterBetterCharacterController.Instance.IsInWater(); if(m_inAir && l_grounded && !l_inWater && (BetterBetterCharacterController.Instance.characterMovement.landedVelocity.magnitude >= Settings.FallLimit)) Ragdoll(); m_inAir = !(l_grounded || l_inWater); } if(!m_ragdolled && BetterBetterCharacterController.Instance.IsSitting()) // Those seats without velocity, smh { CVRSeat l_seat = BetterBetterCharacterController.Instance.GetCurrentSeat(); if(l_seat != null) { Vector3 l_pos = l_seat.transform.position; m_seatVelocity = (l_pos - m_lastSeatPositon) / Time.deltaTime; m_lastSeatPositon = l_pos; } } if(m_ragdolled) { BodySystem.TrackingPositionWeight = 0f; BetterBetterCharacterController.Instance.PauseGroundConstraint(); BetterBetterCharacterController.Instance.ResetAllForces(); PlayerSetup.Instance.AnimatorManager.CancelEmote = true; } if(!m_ragdolled && !m_reachedGround && (BetterBetterCharacterController.Instance.IsOnGround() || BetterBetterCharacterController.Instance.IsInWater() || BetterBetterCharacterController.Instance.IsSitting())) { m_groundedTime += Time.unscaledDeltaTime; if(m_groundedTime >= 0.25f) m_reachedGround = true; } if(m_ragdolled && Settings.AutoRecover) { m_downTime += Time.unscaledDeltaTime; if(m_downTime >= Settings.RecoverDelay) { Unragdoll(); m_downTime = float.MinValue; // One attempt to recover } } if((m_avatarRagdollToggle != null) && m_avatarRagdollToggle.isActiveAndEnabled && m_avatarRagdollToggle.shouldOverride && (m_ragdolled != m_avatarRagdollToggle.isOn)) SwitchRagdoll(); if(Settings.Hotkey && Input.GetKeyDown(Settings.HotkeyKey) && !ViewManager.Instance.IsAnyMenuOpen) SwitchRagdoll(); if(m_ragdolled && CVRInputManager.Instance.jump && Settings.JumpRecover) Unragdoll(); } } void LateUpdate() { if(m_avatarReady) { if(m_ragdolled) { MovePlayer(); foreach(var l_link in m_boneLinks) l_link.Item1.CopyGlobal(l_link.Item2); } else { if(m_vrIK == null) { m_puppetRoot.position = m_avatarTransform.position; m_puppetRoot.rotation = m_avatarTransform.rotation; foreach(var l_link in m_boneLinks) l_link.Item2.CopyGlobal(l_link.Item1); } } } } // Game events void OnAvatarClear(CVRAvatar p_avatar) { try { if(m_initTask != null) { StopCoroutine(m_initTask); m_initTask = null; } if(m_ragdolled) { TryRestoreMovement(); BodySystem.TrackingPositionWeight = 1f; } if(m_puppetRoot != null) Object.Destroy(m_puppetRoot.gameObject); m_puppetRoot = null; m_avatarTransform = null; m_hips = null; m_vrIK = null; m_applyHipsPosition = false; m_ragdolled = false; m_avatarReady = false; m_avatarRagdollToggle = null; m_ragdolledParameter = null; m_puppetReferences = new BipedRagdollReferences(); m_ragdollBodyHandlers.Clear(); m_boneLinks.Clear(); m_jointAnchors.Clear(); m_reachedGround = true; m_groundedTime = 0f; m_downTime = float.MinValue; m_puppet.localScale = Vector3.one; m_inAir = false; } catch(System.Exception e) { MelonLoader.MelonLogger.Error(e); } } void OnAvatarSetup(CVRAvatar p_avatar) { try { if(PlayerSetup.Instance.Animator.isHuman) { m_avatarTransform = PlayerSetup.Instance.AvatarTransform; m_hips = PlayerSetup.Instance.Animator.GetBoneTransform(HumanBodyBones.Hips); IKSystem.Instance.SetAvatarPose(IKSystem.AvatarPose.TPose); PlayerSetup.Instance.AvatarTransform.localPosition = Vector3.zero; PlayerSetup.Instance.AvatarTransform.localRotation = Quaternion.identity; BipedRagdollReferences l_avatarReferences = BipedRagdollReferences.FromAvatar(PlayerSetup.Instance.Animator); m_puppetRoot = new GameObject("Root").transform; m_puppetRoot.gameObject.layer = CVRLayers.PlayerClone; m_puppetRoot.parent = m_puppet; m_puppetRoot.position = m_avatarTransform.position; m_puppetRoot.rotation = m_avatarTransform.rotation; m_puppetReferences.root = m_puppetRoot; 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 m_puppetRoot.position = Vector3.zero; m_puppetRoot.rotation = Quaternion.identity; BipedRagdollCreator.Options l_options = BipedRagdollCreator.AutodetectOptions(m_puppetReferences); l_options.joints = RagdollCreator.JointType.Character; l_options.fixFootColliderRotation = false; BipedRagdollCreator.Create(m_puppetReferences, l_options); Transform[] l_puppetTransforms = m_puppetReferences.GetRagdollTransforms(); Transform[] l_avatarTransforms = l_avatarReferences.GetRagdollTransforms(); Transform[] l_influencedTransforms = new Transform[] { m_puppetReferences.hips, m_puppetReferences.spine, m_puppetReferences.chest }; for(int i = 0; i < l_puppetTransforms.Length; i++) { if(l_puppetTransforms[i] != null) { 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)); } Rigidbody l_body = l_puppetTransforms[i].GetComponent(); Collider l_collider = l_puppetTransforms[i].GetComponent(); if((l_body != null) && (l_collider != null)) { RagdollBodypartHandler l_handler = l_puppetTransforms[i].gameObject.AddComponent(); l_handler.UseBuoyancy = Utils.IsObjectInArray(l_puppetTransforms[i], l_influencedTransforms); m_ragdollBodyHandlers.Add(l_handler); } if(l_avatarTransforms[i] != null) m_boneLinks.Add(System.Tuple.Create(l_puppetTransforms[i], l_avatarTransforms[i])); } } m_vrIK = PlayerSetup.Instance.AvatarObject.GetComponent(); if(m_vrIK != null) m_vrIK.onPostSolverUpdate.AddListener(this.OnIKPostSolverUpdate); m_avatarRagdollToggle = PlayerSetup.Instance.AvatarObject.GetComponentInChildren(true); m_ragdolledParameter = new AvatarParameter("Ragdolled", PlayerSetup.Instance.AnimatorManager); m_initTask = StartCoroutine(WaitForBodyHandlers()); } } catch(System.Exception e) { MelonLoader.MelonLogger.Error(e); } } IEnumerator WaitForBodyHandlers() { while(!m_ragdollBodyHandlers.TrueForAll(p => p.IsReady())) yield return null; foreach(RagdollBodypartHandler l_handler in m_ragdollBodyHandlers) { l_handler.RemovePhysicsController(); l_handler.SetAsKinematic(true); l_handler.SetColliderMaterial(m_physicsMaterial); } // And return back m_puppetRoot.position = m_avatarTransform.position; m_puppetRoot.rotation = m_avatarTransform.rotation; m_avatarReady = true; m_initTask = null; OnMovementDragChanged(Settings.MovementDrag); OnAngularDragChanged(Settings.AngularDrag); OnGravityChanged(Settings.Gravity); OnBuoyancyChanged(Settings.Buoyancy); } void OnAvatarPreReuse() { m_forcedSwitch = true; Unragdoll(); m_forcedSwitch = false; } void OnAvatarPostReuse() { m_vrIK = PlayerSetup.Instance.AvatarObject.GetComponent(); if(m_vrIK != null) m_vrIK.onPostSolverUpdate.AddListener(this.OnIKPostSolverUpdate); } void OnAvatarScaling(float p_scaleDifference) { if(m_puppetRoot != null) m_puppetRoot.localScale = Vector3.one * p_scaleDifference; foreach(var l_pair in m_jointAnchors) l_pair.Item1.connectedAnchor = l_pair.Item2 * p_scaleDifference; } void OnSeatPreSit(CVRSeat p_seat) { m_lastSeatPositon = p_seat.transform.position; if(!p_seat.occupied) { m_forcedSwitch = true; Unragdoll(); m_forcedSwitch = false; m_inAir = false; } } void OnCalibrationStart() { m_forcedSwitch = true; Unragdoll(); m_forcedSwitch = false; m_inAir = false; } void OnWorldPreSpawn() { Unragdoll(); m_inAir = false; OnGravityChanged(Settings.Gravity); OnPhysicsMaterialChanged(true); OnMovementDragChanged(Settings.MovementDrag); OnBuoyancyChanged(Settings.Buoyancy); } void OnCombatPreDown() { if(CombatSystem.Instance.isDown && Settings.CombatReaction) { m_reachedGround = true; m_forcedSwitch = true; Ragdoll(); m_forcedSwitch = false; m_inAir = false; } } void OnFlightChange() { if(BetterBetterCharacterController.Instance.IsFlying()) { m_forcedSwitch = true; Unragdoll(); m_forcedSwitch = false; m_inAir = false; } } void OnPlayerTeleport(BetterBetterCharacterController.PlayerMoveOffset p_offset) { try { m_inAir = false; if(m_avatarReady && m_ragdolled) { m_puppetReferences.hips.position = m_hips.position; m_lastRagdollPosition = m_puppetReferences.hips.position; } } catch(System.Exception e) { MelonLoader.MelonLogger.Error(e); } } void OnIKOffsetUpdate(GameEvents.EventResult p_result) { p_result.m_result |= (m_ragdolled && (m_vrIK != null)); } // VRIK updates void OnIKPostSolverUpdate() { if(!m_ragdolled) { m_puppetRoot.position = m_avatarTransform.position; m_puppetRoot.rotation = m_avatarTransform.rotation; foreach(var l_link in m_boneLinks) l_link.Item2.CopyGlobal(l_link.Item1); } } // Settings void OnMovementDragChanged(float p_value) { if(m_avatarReady) { float l_drag = (WorldManager.IsSafeWorld() ? p_value : 1f); foreach(RagdollBodypartHandler l_handler in m_ragdollBodyHandlers) l_handler.SetDrag(l_drag); } } void OnAngularDragChanged(float p_value) { if(m_avatarReady) { foreach(RagdollBodypartHandler l_handler in m_ragdollBodyHandlers) l_handler.SetAngularDrag(p_value); } } void OnGravityChanged(bool p_state) { if(m_avatarReady) { bool l_gravity = (!WorldManager.IsSafeWorld() || p_state); foreach(RagdollBodypartHandler l_handler in m_ragdollBodyHandlers) l_handler.SetActiveGravity(l_gravity); if(!l_gravity) { OnMovementDragChanged(Settings.MovementDrag); OnAngularDragChanged(Settings.AngularDrag); } } } void OnPhysicsMaterialChanged(bool p_state) { if(m_physicsMaterial != null) { bool l_slipperiness = (Settings.Slipperiness && WorldManager.IsSafeWorld()); bool l_bounciness = (Settings.Bounciness && WorldManager.IsSafeWorld()); m_physicsMaterial.dynamicFriction = (l_slipperiness ? 0f : c_defaultFriction); m_physicsMaterial.staticFriction = (l_slipperiness ? 0f : c_defaultFriction); 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); } } void OnBuoyancyChanged(bool p_state) { if(m_avatarReady) { bool l_buoyancy = (!WorldManager.IsSafeWorld() || p_state); foreach(RagdollBodypartHandler l_handler in m_ragdollBodyHandlers) l_handler.SetBuoyancy(l_buoyancy); if(!l_buoyancy) { OnMovementDragChanged(Settings.MovementDrag); OnAngularDragChanged(Settings.AngularDrag); } } } void OnFallDamageChanged(bool p_state) { m_inAir = false; } void OnGestureGrabChanged(bool p_state) { if(m_avatarReady && m_ragdolled && !p_state) { foreach(var l_hanlder in m_ragdollBodyHandlers) l_hanlder.Detach(); } } // Arbitrary public bool IsRagdolled() => (m_avatarReady && m_ragdolled); public void SwitchRagdoll() { if(m_ragdolled) Unragdoll(); else Ragdoll(); } public void Ragdoll() { if(m_avatarReady && !m_ragdolled && CanRagdoll()) { Vector3 l_velocity = (BetterBetterCharacterController.Instance.IsSitting() ? m_seatVelocity : BetterBetterCharacterController.Instance.velocity); l_velocity *= (WorldManager.IsSafeWorld() ? Settings.VelocityMultiplier : 1f); l_velocity = Vector3.ClampMagnitude(l_velocity, WorldManager.GetMovementLimit()); if(Settings.ViewVelocity && WorldManager.IsSafeWorld()) { float l_mag = l_velocity.magnitude; l_velocity = PlayerSetup.Instance.activeCam.transform.forward * l_mag; } Vector3 l_playerPos = PlayerSetup.Instance.transform.position; Quaternion l_playerRot = PlayerSetup.Instance.transform.rotation; bool l_wasSitting = BetterBetterCharacterController.Instance.IsSitting(); if(BetterBetterCharacterController.Instance.IsSitting()) { BetterBetterCharacterController.Instance.SetSitting(false); l_wasSitting = true; } if(BetterBetterCharacterController.Instance.IsFlying()) BetterBetterCharacterController.Instance.ChangeFlight(false, true); BetterBetterCharacterController.Instance.SetImmobilized(true); BetterBetterCharacterController.Instance.ClearFluidVolumes(); BetterBetterCharacterController.Instance.ResetAllForces(); BetterBetterCharacterController.Instance.PauseGroundConstraint(); BodySystem.TrackingPositionWeight = 0f; m_applyHipsPosition = IKSystem.Instance.applyOriginalHipPosition; IKSystem.Instance.applyOriginalHipPosition = true; m_applyHipsRotation = IKSystem.Instance.applyOriginalHipRotation; IKSystem.Instance.applyOriginalHipRotation = true; PlayerSetup.Instance.AnimatorManager.CancelEmote = true; m_ragdolledParameter.SetValue(true); if(!WorldManager.IsSafeWorld()) { m_reachedGround = false; // Force player to unragdoll and reach ground first m_groundedTime = 0f; } foreach(RagdollBodypartHandler l_handler in m_ragdollBodyHandlers) l_handler.SetAsKinematic(false); m_puppet.gameObject.SetActive(false); // Resets rigidbodies and joints inner physics states m_puppet.gameObject.SetActive(true); foreach(RagdollBodypartHandler l_handler in m_ragdollBodyHandlers) { l_handler.SetVelocity(l_velocity); l_handler.SetAngularVelocity(Vector3.zero); } if(l_wasSitting) { PlayerSetup.Instance.transform.position = l_playerPos; PlayerSetup.Instance.transform.rotation = l_playerRot; } m_lastRagdollPosition = m_puppetReferences.hips.position; m_downTime = 0f; m_ragdolled = true; } } public void Unragdoll() { if(m_avatarReady && m_ragdolled && CanUnragdoll()) { BetterBetterCharacterController.Instance.TeleportPlayerTo(m_puppetReferences.hips.position, PlayerSetup.Instance.GetPlayerRotation().eulerAngles, false, true); TryRestoreMovement(); BodySystem.TrackingPositionWeight = 1f; IKSystem.Instance.applyOriginalHipPosition = m_applyHipsPosition; IKSystem.Instance.applyOriginalHipRotation = m_applyHipsRotation; if(m_vrIK != null) m_vrIK.solver.Reset(); m_ragdolledParameter.SetValue(false); m_puppet.localPosition = Vector3.zero; m_puppet.localRotation = Quaternion.identity; foreach(RagdollBodypartHandler l_handler in m_ragdollBodyHandlers) { l_handler.Detach(); l_handler.ClearFluidVolumes(); l_handler.SetAsKinematic(true); } m_downTime = float.MinValue; // Restore rigidbody properties that could be affected by buoyancy OnMovementDragChanged(Settings.MovementDrag); OnAngularDragChanged(Settings.AngularDrag); m_ragdolled = false; } } bool CanRagdoll() { if(WorldManager.IsRestrictedWorld()) return false; bool l_result = m_reachedGround; l_result &= !BodySystem.isCalibrating; l_result &= ((CombatSystem.Instance == null) || !CombatSystem.Instance.isDown); return (l_result || m_forcedSwitch); } bool CanUnragdoll() { bool l_result = true; l_result &= ((CombatSystem.Instance == null) || !CombatSystem.Instance.isDown); return (l_result || m_forcedSwitch); } void MovePlayer() { // Pain Vector3 l_up = PlayerSetup.Instance.transform.rotation * Vector3.up; Vector3 l_diff = m_puppetReferences.hips.position - m_lastRagdollPosition; m_playerPlane.SetNormalAndPosition(l_up, PlayerSetup.Instance.transform.position); PlayerSetup.Instance.transform.position += l_diff; m_lastRagdollPosition = m_puppetReferences.hips.position; // Try to tether player position closer to hips rigid body position if(m_playerPlane.GetDistanceToPoint(m_lastRagdollPosition) < 0f) m_playerPlane.SetNormalAndPosition(l_up, m_lastRagdollPosition); if(m_playerPlane.GetDistanceToPoint(PlayerSetup.Instance.transform.position) < 0f) PlayerSetup.Instance.transform.position = m_playerPlane.ClosestPointOnPlane(PlayerSetup.Instance.transform.position); } static void TryRestoreMovement() { bool l_state = true; l_state &= ((CombatSystem.Instance == null) || !CombatSystem.Instance.isDown); l_state &= !BetterBetterCharacterController.Instance.IsSitting(); if(l_state) BetterBetterCharacterController.Instance.SetImmobilized(false); } static Transform CloneTransform(Transform p_source, Transform p_parent, string p_name) { Transform l_target = new GameObject(p_name).transform; l_target.gameObject.layer = CVRLayers.PlayerClone; l_target.parent = p_parent; p_source.CopyGlobal(l_target); return l_target; } } }