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