using System.Collections; using System.Collections.Generic; using Invector; using Invector.vCharacterController; using Invector.vEventSystems; using UnityEngine; namespace Beyond { public class bThirdPersonController : vThirdPersonController { protected bool m_isDashing; protected bool m_GodMode = false; public bool m_ignoreTriggers = true; [Header("Beyond's Custom Settings")] [Tooltip("When 'Use RootMotion' is checked, and this is true, the animation's root motion will control character rotation (for 8-way directional movement). If false, the script will rotate the character to face the input direction.")] public bool useAnimationBasedRotation = false; [Header("Beyond's Strafe Combat Settings")] [Tooltip("The minimum horizontal input value to trigger a side roll instead of a forward/backward one.")] [Range(0.1f, 1.0f)] public float strafeRollInputThreshold = 0.3f; [Tooltip("The name of the animation state to play when rolling forward while strafing. (This is now disabled)")] public string strafeRollForwardAnim = "Roll_Forward"; // Kept for reference, but won't be used [Tooltip("The name of the animation state to play when rolling backward while strafing.")] public string strafeRollBackwardAnim = "Roll_Backward"; [Tooltip("The name of the animation state to play when rolling left while strafing.")] public string strafeRollLeftAnim = "Roll_Left"; [Tooltip("The name of the animation state to play when rolling right while strafing.")] public string strafeRollRightAnim = "Roll_Right"; public bool GodMode { get => m_GodMode; set { m_GodMode = value; isImmortal = m_GodMode; } } public bool IsDashingOrRolling() { return m_isDashing || isRolling; } protected override void Start() { base.Start(); } protected override void RollBehavior() { // If we are not strafing, use the default Invector roll behavior. if (!isStrafing) { base.RollBehavior(); return; } // --- Custom Strafe Roll with Root Motion --- if (!isRolling) { return; } // We apply the root motion position change directly. Vector3 deltaPosition = new Vector3(animator.deltaPosition.x, 0f, animator.deltaPosition.z); Vector3 v = (deltaPosition / Time.deltaTime) * (1f - stopMoveWeight); // Apply gravity to the roll if enabled if (rollUseGravity && animator.GetNormalizedTime(baseLayer) >= rollUseGravityTime) { v.y = _rigidbody.linearVelocity.y; } _rigidbody.linearVelocity = v; } public override void Roll() { // If we are strafing, use our custom directional logic. if (isStrafing) { TriggerStrafeRoll(strafeRollForwardAnim, strafeRollBackwardAnim, strafeRollLeftAnim, strafeRollRightAnim); } else // Otherwise, use the default free-locomotion roll. { OnRoll.Invoke(); isRolling = true; animator.CrossFadeInFixedTime("Roll", rollTransition, baseLayer); ReduceStamina(rollStamina, false); currentStaminaRecoveryDelay = 2f; } } public virtual void Dash() { // The Dash logic now mirrors the Roll logic. if (isStrafing) { TriggerStrafeRoll(strafeRollForwardAnim, strafeRollBackwardAnim, strafeRollLeftAnim, strafeRollRightAnim); } else { OnRoll.Invoke(); isRolling = true; animator.CrossFadeInFixedTime("Dash", rollTransition, baseLayer); ReduceStamina(rollStamina, false); currentStaminaRecoveryDelay = 2f; } } // This is the private helper method that contains the core logic for this feature. private void TriggerStrafeRoll(string forwardAnim, string backwardAnim, string leftAnim, string rightAnim) { OnRoll.Invoke(); isRolling = true; ReduceStamina(rollStamina, false); currentStaminaRecoveryDelay = 2f; string animToPlay; // Prioritize side rolls based on horizontal input. if (Mathf.Abs(horizontalSpeed) > strafeRollInputThreshold) { animToPlay = horizontalSpeed > 0 ? rightAnim : leftAnim; } // If horizontal input is not met, always default to the backward roll. // This effectively blocks the forward roll. else { animToPlay = backwardAnim; } animator.CrossFadeInFixedTime(animToPlay, rollTransition, baseLayer); } public void OnEvadeStart() { if (!m_GodMode) isImmortal = true; } public void OnEvadeEnd() { if (!m_GodMode) isImmortal = false; } public override void ActionsControl() { base.ActionsControl(); m_isDashing = IsAnimatorTag("IsDashing"); } protected override void DeadAnimation() { if (!isDead) return; if (!triggerDieBehaviour) { triggerDieBehaviour = true; DeathBehaviour(); } if (deathBy == DeathBy.Animation) { int deadLayer = 0; var info = animatorStateInfos.GetStateInfoUsingTag("Dead"); if (info != null) { if (!animator.IsInTransition(deadLayer) && info.normalizedTime >= 0.99f && groundDistance <= 0.15f) RemoveComponents(); } } else if (deathBy == DeathBy.AnimationWithRagdoll) { int deadLayer = 0; var info = animatorStateInfos.GetStateInfoUsingTag("Dead"); if (info != null) { if (!animator.IsInTransition(deadLayer) && info.normalizedTime >= 0.8f) onActiveRagdoll.Invoke(null); } } else if (deathBy == DeathBy.Ragdoll) { onActiveRagdoll.Invoke(null); } } public void RemoveAnimatorTags() { animatorStateInfos.stateInfos.vToList().ForEach(infos => infos.tags.Clear()); } public override void ControlAnimatorRootMotion() { if (!this.enabled) return; if (isRolling) { RollBehavior(); return; } if (customAction || lockAnimMovement) { StopCharacterWithLerp(); transform.position = animator.rootPosition; transform.rotation = animator.rootRotation; } else if (IsAnimatorTag("Attack")) { if (lockRotation) StopCharacterWithLerp(); transform.position = animator.rootPosition; } if (useRootMotion) MoveCharacter(moveDirection); } protected override void OnTriggerEnter(Collider other) { if (!m_ignoreTriggers) onActionEnter.Invoke(other); } protected override void OnTriggerStay(Collider other) { try { CheckForAutoCrouch(other); } catch (UnityException e) { Debug.LogWarning(e.Message); } if (!m_ignoreTriggers) base.OnTriggerStay(other); } protected override void OnTriggerExit(Collider other) { AutoCrouchExit(other); if (!m_ignoreTriggers) base.OnTriggerExit(other); } void DrawWeaponLowLeft() { } public override void ControlRotationType() { if (lockAnimRotation || lockRotation || customAction || isRolling) return; bool validInput = input != Vector3.zero || (isStrafing ? strafeSpeed.rotateWithCamera : freeSpeed.rotateWithCamera); if (validInput) { if (lockAnimMovement) { inputSmooth = Vector3.Lerp(inputSmooth, input, (isStrafing ? strafeSpeed.movementSmooth : freeSpeed.movementSmooth) * Time.deltaTime); } Vector3 dir = (isStrafing && isGrounded && (!isSprinting || sprintOnlyFree == false) || (freeSpeed.rotateWithCamera && input == Vector3.zero)) && rotateTarget ? rotateTarget.forward : moveDirection; if (isStrafing || !useAnimationBasedRotation) RotateToDirection(dir); } } public override void UpdateAnimatorParameters() { if (disableAnimations) return; animator.SetBool(vAnimatorParameters.IsStrafing, isStrafing); animator.SetBool(vAnimatorParameters.IsSprinting, isSprinting); animator.SetBool(vAnimatorParameters.IsSliding, isSliding && !isRolling); animator.SetBool(vAnimatorParameters.IsCrouching, isCrouching); animator.SetBool(vAnimatorParameters.IsGrounded, isGrounded); animator.SetBool(vAnimatorParameters.IsDead, isDead); animator.SetFloat(vAnimatorParameters.GroundDistance, groundDistance); animator.SetFloat(vAnimatorParameters.GroundAngle, GroundAngleFromDirection()); if (!isGrounded) animator.SetFloat(vAnimatorParameters.VerticalVelocity, verticalVelocity); { if (isStrafing) { animator.SetFloat(vAnimatorParameters.InputHorizontal, horizontalSpeed, strafeSpeed.animationSmooth, Time.fixedDeltaTime); animator.SetFloat(vAnimatorParameters.InputVertical, verticalSpeed, strafeSpeed.animationSmooth, Time.fixedDeltaTime); } else { animator.SetFloat(vAnimatorParameters.InputVertical, verticalSpeed, freeSpeed.animationSmooth, Time.fixedDeltaTime); animator.SetFloat(vAnimatorParameters.InputHorizontal, useAnimationBasedRotation ? horizontalSpeed : 0, freeSpeed.animationSmooth, Time.fixedDeltaTime); } animator.SetFloat(vAnimatorParameters.InputMagnitude, Mathf.LerpUnclamped(inputMagnitude, 0f, stopMoveWeight), isStrafing ? strafeSpeed.animationSmooth : freeSpeed.animationSmooth, Time.fixedDeltaTime); if (useLeanMovementAnim && inputMagnitude >= 0.1f) { animator.SetFloat(vAnimatorParameters.RotationMagnitude, rotationMagnitude, leanSmooth, Time.fixedDeltaTime); } else if (useTurnOnSpotAnim && inputMagnitude < 0.1f) { animator.SetFloat(vAnimatorParameters.RotationMagnitude, (float)System.Math.Round(rotationMagnitude, 2), rotationMagnitude == 0 ? 0.1f : 0.01f, Time.fixedDeltaTime); } } } } }