557 lines
13 KiB
C#
557 lines
13 KiB
C#
using Cinemachine;
|
|
using System.Collections;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
|
|
[RequireComponent(typeof(CharacterController))]
|
|
public class PlayerControllerNewRohan : MonoBehaviour
|
|
{
|
|
[Header("Movement")]
|
|
[SerializeField] private float walkSpeed = 3f;
|
|
[SerializeField] private float runSpeed = 6f;
|
|
[SerializeField] private float rotationSpeed = 10f;
|
|
private Stamina stamina;
|
|
|
|
[Header("References")]
|
|
private CharacterController controller;
|
|
private Animator animator;
|
|
|
|
[Header("State")]
|
|
private bool isAttacking;
|
|
private bool canMove = true;
|
|
|
|
[Header("Gravity")]
|
|
[SerializeField] private float gravity = -9.81f;
|
|
[SerializeField] private float groundCheckDistance = 0.2f;
|
|
|
|
[Header("Combat")]
|
|
public float attackRange = 3f; // adjust in inspector
|
|
|
|
private float verticalVelocity;
|
|
|
|
// Input
|
|
private Vector2 moveInput;
|
|
private bool sprintInput;
|
|
|
|
private float currentSpeed;
|
|
public Joystick joystick;
|
|
|
|
public GameObject rightHandPunchHitbox;
|
|
public GameObject leftHandPunchHitbox;
|
|
public GameObject rightFootKickHitbox;
|
|
public GameObject leftFootKickHitbox;
|
|
|
|
public TrailRenderer rightHandTrail;
|
|
public TrailRenderer leftHandTrail;
|
|
public TrailRenderer rightFootTrail;
|
|
public TrailRenderer leftFootTrail;
|
|
|
|
public Transform target; // assign enemy in inspector
|
|
|
|
[Header("Camera FOV")]
|
|
[SerializeField] private CinemachineFreeLook freeLookCam;
|
|
|
|
[SerializeField] private float normalFOV = 60f;
|
|
[SerializeField] private float sprintFOV = 75f;
|
|
[SerializeField] private float fovSpeed = 5f;
|
|
|
|
[SerializeField] private float walkStepInterval = 0.5f;
|
|
[SerializeField] private float runStepInterval = 0.3f;
|
|
|
|
private float footstepTimer;
|
|
|
|
private Coroutine attackRoutine;
|
|
|
|
private bool isDepositing;
|
|
private Coroutine depositRoutine;
|
|
|
|
private bool isOpeningVault;
|
|
|
|
[Header("Deposit Effect")]
|
|
[SerializeField] private ParticleSystem depositAura;
|
|
|
|
void Awake()
|
|
{
|
|
controller = GetComponent<CharacterController>();
|
|
animator = GetComponent<Animator>();
|
|
stamina = GetComponent<Stamina>();
|
|
|
|
StopDepositEffect();
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
//Cursor.lockState = CursorLockMode.Locked;
|
|
//Cursor.visible = false;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
HandleMovement();
|
|
}
|
|
|
|
// ---------------- MOVEMENT ----------------
|
|
void HandleMovement()
|
|
{
|
|
if (!canMove || isAttacking) return;
|
|
|
|
// -------- CAMERA RELATIVE MOVE --------
|
|
Vector3 camForward = Camera.main.transform.forward;
|
|
Vector3 camRight = Camera.main.transform.right;
|
|
|
|
camForward.y = 0;
|
|
camRight.y = 0;
|
|
|
|
camForward.Normalize();
|
|
camRight.Normalize();
|
|
|
|
Vector3 move = camForward * moveInput.y + camRight * moveInput.x;
|
|
|
|
bool isMoving = move.magnitude > 0.1f;
|
|
|
|
if (isMoving)
|
|
SetTrails(true);
|
|
else if (!isAttacking)
|
|
SetTrails(false);
|
|
|
|
// Sprint only if moving
|
|
bool isRunning = sprintInput && isMoving && stamina.currentStamina > 0f;
|
|
|
|
if (isRunning)
|
|
{
|
|
stamina.UseStamina(20f * Time.deltaTime);
|
|
}
|
|
else if (!isAttacking && !sprintInput) // key fix
|
|
{
|
|
stamina.RecoverStamina(10f * Time.deltaTime);
|
|
}
|
|
|
|
HandleFootsteps(isMoving, isRunning);
|
|
|
|
if (freeLookCam != null)
|
|
{
|
|
float targetFOV = isRunning ? sprintFOV : normalFOV;
|
|
|
|
// Apply to all rigs (top, middle, bottom)
|
|
freeLookCam.m_Lens.FieldOfView = Mathf.Lerp(
|
|
freeLookCam.m_Lens.FieldOfView,
|
|
targetFOV,
|
|
Time.deltaTime * fovSpeed
|
|
);
|
|
}
|
|
|
|
currentSpeed = isRunning ? runSpeed : walkSpeed;
|
|
|
|
// -------- GRAVITY --------
|
|
if (controller.isGrounded && verticalVelocity < 0)
|
|
{
|
|
verticalVelocity = -2f; // keeps player grounded
|
|
}
|
|
|
|
verticalVelocity += gravity * Time.deltaTime;
|
|
|
|
// -------- FINAL MOVE --------
|
|
Vector3 finalMove = move * currentSpeed;
|
|
finalMove.y = verticalVelocity;
|
|
|
|
controller.Move(finalMove * Time.deltaTime);
|
|
|
|
// -------- ROTATION --------
|
|
if (isMoving)
|
|
{
|
|
Quaternion targetRotation = Quaternion.LookRotation(move);
|
|
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
|
|
}
|
|
|
|
// -------- ANIMATION --------
|
|
float targetSpeed = move.magnitude * (isRunning ? 2f : 1f);
|
|
animator.SetFloat("Speed", targetSpeed, 0.1f, Time.deltaTime);
|
|
}
|
|
|
|
|
|
|
|
// ---------------- INPUT CALLBACKS ----------------
|
|
|
|
public void OnMove(InputAction.CallbackContext context)
|
|
{
|
|
moveInput = context.ReadValue<Vector2>();
|
|
}
|
|
|
|
public void OnSprint(InputAction.CallbackContext context)
|
|
{
|
|
sprintInput = context.ReadValueAsButton();
|
|
}
|
|
|
|
// ---------------- ATTACKS ----------------
|
|
|
|
public void OnAttack1(InputAction.CallbackContext ctx)
|
|
{
|
|
if (ctx.performed)
|
|
{
|
|
attackRoutine = StartCoroutine(PlayAttack("Haymaker", 1f));
|
|
}
|
|
}
|
|
|
|
public void OnAttack2(InputAction.CallbackContext ctx)
|
|
{
|
|
if (ctx.performed)
|
|
{
|
|
if (ctx.performed) StartCoroutine(PlayAttack("LeftKick", 1.5f));
|
|
}
|
|
}
|
|
|
|
public void OnAttack3(InputAction.CallbackContext ctx)
|
|
{
|
|
if (ctx.performed)
|
|
{
|
|
if (ctx.performed) StartCoroutine(PlayAttack("LowKick", 1.5f));
|
|
}
|
|
}
|
|
|
|
public void OnAttack4(InputAction.CallbackContext ctx)
|
|
{
|
|
if (ctx.performed)
|
|
{
|
|
if (ctx.performed) StartCoroutine(PlayAttack("ElbowSmash", 0.6f));
|
|
}
|
|
}
|
|
|
|
public void OnAttack5(InputAction.CallbackContext ctx)
|
|
{
|
|
if (ctx.performed)
|
|
{
|
|
if (ctx.performed) StartCoroutine(PlayAttack("SpinningBackFist", 1f));
|
|
}
|
|
}
|
|
|
|
public void OnAttack6(InputAction.CallbackContext ctx)
|
|
{
|
|
if (ctx.performed)
|
|
{
|
|
if (ctx.performed) StartCoroutine(PlayAttack("BicycleKick", 1f));
|
|
}
|
|
}
|
|
|
|
public void PunchTap()
|
|
{
|
|
StartCoroutine(PlayAttack("Haymaker", 1f));
|
|
}
|
|
|
|
public void PunchHold()
|
|
{
|
|
StartCoroutine(PlayAttack("ElbowSmash", 0.6f));
|
|
}
|
|
|
|
public void KickTap()
|
|
{
|
|
StartCoroutine(PlayAttack("LeftKick", 1.5f));
|
|
}
|
|
|
|
public void KickHold()
|
|
{
|
|
StartCoroutine(PlayAttack("LowKick", 1.5f));
|
|
}
|
|
|
|
public void SpecialTap()
|
|
{
|
|
StartCoroutine(PlayAttack("SpinningBackFist", 1f));
|
|
}
|
|
|
|
public void SpecialHold()
|
|
{
|
|
StartCoroutine(PlayAttack("BicycleKick", 1f));
|
|
}
|
|
|
|
IEnumerator PlayAttack(string trigger, float distance)
|
|
{
|
|
CancelVaultOpening();
|
|
|
|
if (isAttacking) yield break;
|
|
|
|
if (!stamina.UseStamina(15f)) yield break;
|
|
|
|
isAttacking = true;
|
|
canMove = false;
|
|
|
|
SetTrails(true);
|
|
|
|
if (IsTargetInRange())
|
|
{
|
|
SetAttackDistance(distance);
|
|
}
|
|
|
|
animator.SetTrigger(trigger);
|
|
|
|
yield return new WaitForSeconds(1f);
|
|
|
|
SetTrails(false);
|
|
|
|
canMove = true;
|
|
isAttacking = false;
|
|
}
|
|
|
|
public void CancelAttack()
|
|
{
|
|
if (!isAttacking) return;
|
|
|
|
if (attackRoutine != null)
|
|
{
|
|
StopCoroutine(attackRoutine);
|
|
attackRoutine = null;
|
|
}
|
|
|
|
isAttacking = false;
|
|
canMove = true;
|
|
|
|
SetTrails(false);
|
|
|
|
// reset attack triggers
|
|
animator.ResetTrigger("Haymaker");
|
|
animator.ResetTrigger("LeftKick");
|
|
animator.ResetTrigger("LowKick");
|
|
animator.ResetTrigger("ElbowSmash");
|
|
animator.ResetTrigger("SpinningBackFist");
|
|
animator.ResetTrigger("BicycleKick");
|
|
|
|
// NO animator.SetTrigger("Hit") here
|
|
}
|
|
|
|
public void PlayWhoosh()
|
|
{
|
|
AudioManager.Instance.PlaySFX("PunchWhoosh");
|
|
}
|
|
|
|
bool IsTargetInRange()
|
|
{
|
|
if (target == null) return false;
|
|
|
|
float dist = Vector3.Distance(transform.position, target.position);
|
|
|
|
return dist <= attackRange;
|
|
}
|
|
|
|
void SetAttackDistance(float distance)
|
|
{
|
|
if (target == null) return;
|
|
|
|
// direction from enemy player
|
|
Vector3 dir = (transform.position - target.position).normalized;
|
|
dir.y = 0;
|
|
|
|
// place player at fixed distance
|
|
Vector3 newPos = target.position + dir * distance;
|
|
|
|
transform.position = newPos;
|
|
}
|
|
|
|
public void StartDeposit(float depositTime)
|
|
{
|
|
if (isDepositing)
|
|
return;
|
|
|
|
StartDepositEffect();
|
|
|
|
depositRoutine = StartCoroutine(DepositRoutine(depositTime));
|
|
}
|
|
|
|
IEnumerator DepositRoutine(float depositTime)
|
|
{
|
|
isDepositing = true;
|
|
|
|
float timer = depositTime;
|
|
|
|
while (timer > 0)
|
|
{
|
|
UI_Manager.Instance?.UpdateDepositTimer(timer);
|
|
|
|
timer -= Time.deltaTime;
|
|
|
|
yield return null;
|
|
}
|
|
|
|
UI_Manager.Instance?.HideDepositTimer();
|
|
|
|
CashManager cash = GetComponent<CashManager>();
|
|
|
|
if (cash != null)
|
|
{
|
|
int depositedAmount = cash.DepositAllCash();
|
|
|
|
TeamMember teamMember = GetComponent<TeamMember>();
|
|
|
|
DepositStationManager.Instance.AddCash(
|
|
depositedAmount,
|
|
teamMember.CurrentTeam
|
|
);
|
|
}
|
|
|
|
StopDepositEffect();
|
|
|
|
canMove = true;
|
|
isDepositing = false;
|
|
}
|
|
|
|
public void CancelDeposit()
|
|
{
|
|
if (!isDepositing)
|
|
return;
|
|
|
|
if (depositRoutine != null)
|
|
{
|
|
StopCoroutine(depositRoutine);
|
|
depositRoutine = null;
|
|
}
|
|
|
|
UI_Manager.Instance?.HideDepositTimer();
|
|
|
|
StopDepositEffect();
|
|
|
|
isDepositing = false;
|
|
}
|
|
|
|
void FaceTarget()
|
|
{
|
|
Vector3 dir = target.position - transform.position;
|
|
dir.y = 0;
|
|
|
|
if (dir.sqrMagnitude > 0.01f)
|
|
{
|
|
transform.rotation = Quaternion.LookRotation(dir);
|
|
}
|
|
}
|
|
|
|
public void PlayVaultOpen(System.Action onComplete)
|
|
{
|
|
StartCoroutine(VaultRoutine(onComplete));
|
|
}
|
|
|
|
IEnumerator VaultRoutine(System.Action onComplete)
|
|
{
|
|
canMove = false;
|
|
isOpeningVault = true;
|
|
|
|
animator.SetTrigger("OpenVault");
|
|
|
|
float timer = 6f;
|
|
|
|
while (timer > 0)
|
|
{
|
|
if (!isOpeningVault)
|
|
{
|
|
UI_Manager.Instance?.HideActionTimer();
|
|
|
|
canMove = true;
|
|
|
|
yield break;
|
|
}
|
|
|
|
UI_Manager.Instance?.UpdateActionTimer(
|
|
"Opening Vault",
|
|
timer);
|
|
|
|
timer -= Time.deltaTime;
|
|
|
|
yield return null;
|
|
}
|
|
|
|
UI_Manager.Instance?.HideActionTimer();
|
|
|
|
canMove = true;
|
|
isOpeningVault = false;
|
|
|
|
onComplete?.Invoke();
|
|
}
|
|
|
|
public void CancelVaultOpening()
|
|
{
|
|
if (!isOpeningVault)
|
|
return;
|
|
|
|
isOpeningVault = false;
|
|
|
|
animator.ResetTrigger("OpenVault");
|
|
|
|
canMove = true;
|
|
|
|
UI_Manager.Instance?.HideActionTimer();
|
|
}
|
|
|
|
public void PlayPickupAnimation()
|
|
{
|
|
if (animator != null)
|
|
{
|
|
animator.SetTrigger("Pickup");
|
|
}
|
|
}
|
|
|
|
void SetTrails(bool state)
|
|
{
|
|
rightHandTrail.emitting = state;
|
|
leftHandTrail.emitting = state;
|
|
rightFootTrail.emitting = state;
|
|
leftFootTrail.emitting = state;
|
|
}
|
|
|
|
public void StartDepositEffect()
|
|
{
|
|
if (depositAura != null)
|
|
depositAura.Play();
|
|
}
|
|
|
|
public void StopDepositEffect()
|
|
{
|
|
if (depositAura != null)
|
|
depositAura.Stop();
|
|
}
|
|
|
|
// Disable player movement (call at animation start)
|
|
public void DisableMovement()
|
|
{
|
|
canMove = false;
|
|
}
|
|
|
|
// Enable player movement (call at animation end)
|
|
public void EnableMovement()
|
|
{
|
|
canMove = true;
|
|
}
|
|
|
|
void HandleFootsteps(bool isMoving, bool isRunning)
|
|
{
|
|
if (!controller.isGrounded)
|
|
{
|
|
footstepTimer = 0f;
|
|
return;
|
|
}
|
|
|
|
if (!isMoving)
|
|
{
|
|
footstepTimer = 0f;
|
|
return;
|
|
}
|
|
|
|
float interval = isRunning ? runStepInterval : walkStepInterval;
|
|
|
|
footstepTimer += Time.deltaTime;
|
|
|
|
if (footstepTimer >= interval)
|
|
{
|
|
footstepTimer = 0f;
|
|
|
|
AudioManager.Instance.PlayFootstep(isRunning);
|
|
}
|
|
}
|
|
|
|
public void EnableRightHandPunchHitbox() => rightHandPunchHitbox.SetActive(true);
|
|
public void DisableRightHandPunchHitbox() => rightHandPunchHitbox.SetActive(false);
|
|
|
|
public void EnableLeftHandPunchHitbox() => leftHandPunchHitbox.SetActive(true);
|
|
public void DisableLeftHandPunchHitbox() => leftHandPunchHitbox.SetActive(false);
|
|
|
|
public void EnableLeftFootKickHitbox() => leftFootKickHitbox.SetActive(true);
|
|
public void DisableLeftFootKickHitbox() => leftFootKickHitbox.SetActive(false);
|
|
|
|
public void EnableRightFootKickHitbox() => rightFootKickHitbox.SetActive(true);
|
|
public void DisableRightFootKickHitbox() => rightFootKickHitbox.SetActive(false);
|
|
} |