310 lines
13 KiB
C#
310 lines
13 KiB
C#
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 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";
|
|
|
|
// --- UPDATED FIELDS FOR FINER CONTROL ---
|
|
[Header("Beyond's Strafe Roll Correction")]
|
|
[Tooltip("An additional angle to apply for a LEFT strafe roll.")]
|
|
[Range(-90f, 90f)]
|
|
public float strafeRollLeftCorrectionAngle = -45f;
|
|
|
|
[Tooltip("An additional angle to apply for a RIGHT strafe roll.")]
|
|
[Range(-90f, 90f)]
|
|
public float strafeRollRightCorrectionAngle = 45f;
|
|
|
|
[Tooltip("An additional angle to apply for a BACKWARD strafe roll. Use 0 for a standard 180-degree roll.")]
|
|
[Range(-180f, 180f)]
|
|
public float strafeRollBackwardCorrectionAngle = 0f;
|
|
// --- END OF UPDATED FIELDS ---
|
|
|
|
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 (!isStrafing)
|
|
{
|
|
base.RollBehavior();
|
|
return;
|
|
}
|
|
if (!isRolling)
|
|
{
|
|
return;
|
|
}
|
|
Vector3 deltaPosition = new Vector3(animator.deltaPosition.x, 0f, animator.deltaPosition.z);
|
|
Vector3 v = (deltaPosition / Time.deltaTime) * (1f - stopMoveWeight);
|
|
if (rollUseGravity && animator.GetNormalizedTime(baseLayer) >= rollUseGravityTime)
|
|
{
|
|
v.y = _rigidbody.linearVelocity.y;
|
|
}
|
|
_rigidbody.linearVelocity = v;
|
|
}
|
|
|
|
public override void Roll()
|
|
{
|
|
if (isStrafing)
|
|
{
|
|
TriggerStrafeRoll(strafeRollForwardAnim, strafeRollBackwardAnim, strafeRollLeftAnim, strafeRollRightAnim);
|
|
}
|
|
else
|
|
{
|
|
OnRoll.Invoke();
|
|
isRolling = true;
|
|
animator.CrossFadeInFixedTime("Roll", rollTransition, baseLayer);
|
|
ReduceStamina(rollStamina, false);
|
|
currentStaminaRecoveryDelay = 2f;
|
|
}
|
|
}
|
|
|
|
public virtual void Dash()
|
|
{
|
|
if (isStrafing)
|
|
{
|
|
TriggerStrafeRoll(strafeRollForwardAnim, strafeRollBackwardAnim, strafeRollLeftAnim, strafeRollRightAnim);
|
|
}
|
|
else
|
|
{
|
|
OnRoll.Invoke();
|
|
isRolling = true;
|
|
animator.CrossFadeInFixedTime("Dash", rollTransition, baseLayer);
|
|
ReduceStamina(rollStamina, false);
|
|
currentStaminaRecoveryDelay = 2f;
|
|
}
|
|
}
|
|
|
|
// --- FINAL ROBUST METHOD ---
|
|
// This logic now handles all specified cases:
|
|
// 1. Sets a base rotation facing the camera to ensure consistency.
|
|
// 2. Defaults to a backward roll if input is near-zero (fixing the lingering roll issue).
|
|
// 3. Never uses the forward roll; it triggers a backward roll instead.
|
|
// 4. Applies a unique correction angle for backward rolls as well as side rolls.
|
|
private void TriggerStrafeRoll(string forwardAnim, string backwardAnim, string leftAnim, string rightAnim)
|
|
{
|
|
OnRoll.Invoke();
|
|
isRolling = true;
|
|
ReduceStamina(rollStamina, false);
|
|
currentStaminaRecoveryDelay = 2f;
|
|
|
|
string animToPlay;
|
|
float correctionAngle = 0f;
|
|
|
|
// Determine the base "forward" direction from the camera, ignoring vertical tilt.
|
|
Vector3 baseForward = Vector3.Scale(Camera.main.transform.forward, new Vector3(1, 0, 1)).normalized;
|
|
if (baseForward == Vector3.zero) baseForward = transform.forward; // Fallback
|
|
|
|
// Set the character's rotation to this base direction. This provides a clean slate.
|
|
transform.rotation = Quaternion.LookRotation(baseForward);
|
|
|
|
// Check if input is negligible (joystick is centered).
|
|
// input.sqrMagnitude is more efficient than input.magnitude. 0.1*0.1=0.01.
|
|
if (input.sqrMagnitude < 0.01f)
|
|
{
|
|
// If no input, always perform a backward roll.
|
|
animToPlay = backwardAnim;
|
|
correctionAngle = strafeRollBackwardCorrectionAngle;
|
|
}
|
|
else
|
|
{
|
|
// If there IS input, determine the dominant direction.
|
|
if (Mathf.Abs(verticalSpeed) >= Mathf.Abs(horizontalSpeed))
|
|
{
|
|
// Vertical input is dominant.
|
|
// ALWAYS use the backward roll, regardless of forward or backward input.
|
|
animToPlay = backwardAnim;
|
|
correctionAngle = strafeRollBackwardCorrectionAngle;
|
|
}
|
|
else
|
|
{
|
|
// Horizontal input is dominant.
|
|
if (horizontalSpeed > 0)
|
|
{
|
|
animToPlay = rightAnim;
|
|
correctionAngle = strafeRollRightCorrectionAngle;
|
|
}
|
|
else
|
|
{
|
|
animToPlay = leftAnim;
|
|
correctionAngle = strafeRollLeftCorrectionAngle;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply the chosen correction angle if it's not zero.
|
|
if (correctionAngle != 0f)
|
|
{
|
|
transform.Rotate(0, correctionAngle, 0);
|
|
}
|
|
|
|
// Play the selected animation.
|
|
animator.CrossFadeInFixedTime(animToPlay, rollTransition, baseLayer);
|
|
}
|
|
// --- END OF FINAL METHOD ---
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |