188 lines
7.4 KiB
C#
188 lines
7.4 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 Health Logic")]
|
|
public float healthRecoveryCap = 1f;
|
|
|
|
[Header("Beyond's Custom Settings")]
|
|
public bool useAnimationBasedRotation = false;
|
|
|
|
[Header("Beyond's Dash Settings")]
|
|
public string dashBlendTreeState = "Dash_Directional";
|
|
public string dashHorizontalParam = "DashHorizontal";
|
|
public string dashVerticalParam = "DashVertical";
|
|
|
|
[Tooltip("If your animation is slightly rotated (Red arrow offset), add an angle here to compensate (e.g. -45 or 45).")]
|
|
public float dashAngleCorrection = 0f;
|
|
|
|
[Tooltip("Print debug info to Console when dashing")]
|
|
public bool debugDash = true;
|
|
|
|
public bool GodMode
|
|
{
|
|
get => m_GodMode;
|
|
set { m_GodMode = value; isImmortal = m_GodMode; }
|
|
}
|
|
|
|
protected override bool canRecoverHealth
|
|
{
|
|
get
|
|
{
|
|
float limitHP = maxHealth * healthRecoveryCap;
|
|
return base.canRecoverHealth && (_currentHealth < limitHP);
|
|
}
|
|
}
|
|
|
|
public bool IsDashingOrRolling() => m_isDashing || isRolling;
|
|
|
|
protected override void Start()
|
|
{
|
|
base.Start();
|
|
}
|
|
|
|
public override void Roll()
|
|
{
|
|
Dash();
|
|
}
|
|
|
|
public virtual void Dash()
|
|
{
|
|
OnRoll.Invoke();
|
|
isRolling = true;
|
|
ReduceStamina(rollStamina, false);
|
|
currentStaminaRecoveryDelay = 2f;
|
|
|
|
// Setup Camera Vectors
|
|
Transform camT = Camera.main != null ? Camera.main.transform : transform;
|
|
// Flatten camera vectors so looking up/down doesn't affect dash length
|
|
Vector3 camFwd = Vector3.Scale(camT.forward, new Vector3(1, 0, 1)).normalized;
|
|
Vector3 camRight = Vector3.Scale(camT.right, new Vector3(1, 0, 1)).normalized;
|
|
|
|
Vector3 targetWorldDir = Vector3.zero;
|
|
|
|
// 1. Determine Target World Direction
|
|
if (input.sqrMagnitude < 0.1f)
|
|
{
|
|
// NO INPUT -> Move strictly "Into the Screen" (Towards Camera)
|
|
// This is the negative of the Camera Forward vector.
|
|
targetWorldDir = -camFwd;
|
|
if (debugDash) Debug.Log($"[Dash] No Input. Target: BACKWARD ({targetWorldDir})");
|
|
}
|
|
else
|
|
{
|
|
// DIRECTIONAL INPUT
|
|
// FIX: Use input.z for Forward/Backward, NOT input.y
|
|
targetWorldDir = (camFwd * input.z + camRight * input.x).normalized;
|
|
|
|
if (debugDash) Debug.Log($"[Dash] Input: {input}. Target World Dir: {targetWorldDir}");
|
|
}
|
|
|
|
// 2. Convert World Direction to Character Local Space
|
|
// "Where is the Target World Dir relative to ME?"
|
|
Vector3 localDir = transform.InverseTransformDirection(targetWorldDir);
|
|
|
|
// 3. Apply Angle Correction (If animation is skewed 45 degrees)
|
|
if (dashAngleCorrection != 0f)
|
|
{
|
|
localDir = Quaternion.Euler(0, dashAngleCorrection, 0) * localDir;
|
|
}
|
|
|
|
// 4. Normalize (Ensure full blend magnitude)
|
|
localDir.y = 0;
|
|
localDir.Normalize();
|
|
|
|
// 5. Send to Animator (Dedicated Parameters)
|
|
animator.SetFloat(dashHorizontalParam, localDir.x);
|
|
animator.SetFloat(dashVerticalParam, localDir.z);
|
|
|
|
if (debugDash)
|
|
{
|
|
Debug.Log($"[Dash Final] Local X: {localDir.x} | Local Z: {localDir.z}");
|
|
}
|
|
|
|
// 6. Play Animation
|
|
animator.CrossFadeInFixedTime(dashBlendTreeState, rollTransition, baseLayer);
|
|
}
|
|
|
|
// --- Standard Boilerplate Below ---
|
|
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 && !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 && !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);
|
|
}
|
|
}
|
|
}
|
|
} |