Files
beyond/Assets/Scripts/InvectorDerivatives/bThirdPersonController.cs

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);
}
}
}
}
}