Files
beyond/Assets/Scripts/InvectorDerivatives/bThirdPersonController.cs
2026-01-30 17:06:33 +01:00

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