267 lines
11 KiB
C#
267 lines
11 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 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |