Projext/Assets/Scripts/Player/Combat/Attack.cs
2026-02-06 14:32:48 +09:00

182 lines
5.2 KiB
C#

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PlayerAttack : MonoBehaviour
{
[Header("--- 활 설정 ---")]
[SerializeField] private GameObject arrowPrefab;
[SerializeField] private Transform firePoint;
[SerializeField] private PlayerAnimator pAnim;
[Header("--- 일반 공격 (좌클릭) ---")]
[SerializeField] private float normalDamage = 10f;
[SerializeField] private float normalRange = 15f;
[SerializeField] private float normalSpeed = 20f;
[SerializeField] private float attackCooldown = 0.5f;
[Header("--- 차징 공격 (우클릭) ---")]
[SerializeField] private float maxChargeTime = 2.0f;
[System.Serializable]
public struct ChargeStage
{
public float chargeTime;
public float damageMult;
public float rangeMult;
}
[SerializeField] private List<ChargeStage> chargeStages;
private float _lastAttackTime;
private float _chargeTimer;
private bool _isCharging = false;
private bool _isAttacking = false;
// 재장전 잠금 장치 (한 발 쏘면 우클릭 뗄 때까지 발사 불가)
private bool _waitForRelease = false;
private float _pendingDamage;
private float _pendingSpeed;
private float _pendingRange;
public bool IsCharging => _isCharging;
public bool IsAttacking
{
get => _isAttacking;
set => _isAttacking = value;
}
public float ChargeProgress => Mathf.Clamp01(_chargeTimer / maxChargeTime);
private void Start()
{
if (chargeStages == null || chargeStages.Count == 0)
{
chargeStages = new List<ChargeStage>
{
new ChargeStage { chargeTime = 0f, damageMult = 1f, rangeMult = 1f },
new ChargeStage { chargeTime = 1f, damageMult = 1.5f, rangeMult = 1.2f },
new ChargeStage { chargeTime = 2f, damageMult = 2.5f, rangeMult = 1.5f }
};
}
}
private void Update()
{
if (_isCharging)
{
_chargeTimer += Time.deltaTime;
}
}
// --- [1] 일반 공격 ---
public void PerformNormalAttack()
{
if (Time.time < _lastAttackTime + attackCooldown) return;
if (_isAttacking) return;
_pendingDamage = normalDamage;
_pendingSpeed = normalSpeed;
_pendingRange = normalRange;
_lastAttackTime = Time.time;
if (pAnim != null) pAnim.TriggerThrow();
StartCoroutine(AttackRoutine());
}
// --- [2] 차징 시작 ---
public void StartCharging()
{
// 🔒 잠금 상태(방금 쏨)라면 차징 시작 안 함 (손 떼야 풀림)
if (_waitForRelease) return;
_isCharging = true;
_chargeTimer = 0f;
if (pAnim != null) pAnim.SetCharging(true);
if (CinemachineShake.Instance != null) CinemachineShake.Instance.SetZoom(true);
}
// 내부용: 효과 끄기
private void ResetChargingEffects()
{
_isCharging = false;
_chargeTimer = 0f;
if (pAnim != null) pAnim.SetCharging(false);
if (CinemachineShake.Instance != null) CinemachineShake.Instance.SetZoom(false);
}
// 외부 호출: 우클릭 뗐을 때 (여기서 잠금을 풉니다)
public void CancelCharging()
{
ResetChargingEffects();
_waitForRelease = false; // ✅ 잠금 해제! 다시 쏠 수 있음
}
// --- [3] 차징 발사 ---
public void ReleaseAttack()
{
if (!_isCharging) return;
ChargeStage currentStage = chargeStages[0];
foreach (var stage in chargeStages)
{
if (_chargeTimer >= stage.chargeTime) currentStage = stage;
}
_pendingDamage = normalDamage * currentStage.damageMult;
_pendingSpeed = normalSpeed * currentStage.rangeMult;
_pendingRange = normalRange * currentStage.rangeMult;
if (pAnim != null) pAnim.TriggerThrow();
// 🔒 발사했으므로 잠금! (우클릭 뗄 때까지 차징 불가)
_waitForRelease = true;
_lastAttackTime = Time.time;
StartCoroutine(AttackRoutine());
}
// --- [4] 이벤트: 화살 생성 ---
public void OnShootArrow()
{
if (arrowPrefab == null || firePoint == null) return;
GameObject arrow = Instantiate(arrowPrefab, firePoint.position, transform.rotation);
PlayerArrow arrowScript = arrow.GetComponent<PlayerArrow>();
if (arrowScript != null)
{
arrowScript.Initialize(_pendingDamage, _pendingSpeed, _pendingRange);
}
}
// --- [5] 이벤트: 공격 끝 (정상적인 경우) ---
public void OnAttackEnd()
{
_isAttacking = false;
ResetChargingEffects();
}
// --- [6] 안전장치 코루틴 ---
private IEnumerator AttackRoutine()
{
_isAttacking = true;
// 🚨 안전장치: 0.6초 뒤에는 무조건 공격 상태를 풉니다!
// (애니메이션 이벤트 OnAttackEnd가 씹혀도 멈추지 않게 함)
yield return new WaitForSeconds(0.6f);
if (_isAttacking)
{
_isAttacking = false;
ResetChargingEffects();
}
}
public void StartWeaponCollision() { }
public void StopWeaponCollision() { }
}