using System; using System.Collections; using BlueWater.Audios; using BlueWater.Interfaces; using BlueWater.Uis; using BlueWater.Utility; using Sirenix.OdinInspector; using UnityEngine; namespace BlueWater.Players { public class PlayerHealthPoint : MonoBehaviour, IDamageable { // Components [SerializeField] private SpriteRenderer _spriteRenderer; private IDashable _dashable; private ISkillHandler _skillHandler; // Variables [field: SerializeField] public int MaxHealthPoint { get; private set; } [field: SerializeField] public int CurrentHealthPoint { get; private set; } [field: SerializeField] public float InvincibilityDuration { get; private set; } = 0.5f; [field: SerializeField] public bool IsInvincible { get; private set; } private WaitForSeconds _flashWhiteWaitTime; private Coroutine _flashWhiteCoroutine; private Coroutine _damageIntervalCoroutine; private PlayerHealthPointUi _playerHealthPointUi; private bool _isQuitting; // Hashes private static readonly int _isHitHash = Shader.PropertyToID("_IsHit"); // Events public event Action OnHealthChanged; public event Action OnDead; // Unity events private void Awake() { InitializeComponents(); } private void Start() { if (CombatUiManager.Instance) { _playerHealthPointUi = CombatUiManager.Instance.PlayerHealthPointUi; OnHealthChanged += _playerHealthPointUi.SetCurrentHealthPoint; } _flashWhiteWaitTime = new WaitForSeconds(InvincibilityDuration * 0.1f); SetCurrentHealthPoint(MaxHealthPoint); } private void OnApplicationQuit() { _isQuitting = true; } private void OnDestroy() { if (_isQuitting || !_playerHealthPointUi) return; OnHealthChanged -= _playerHealthPointUi.SetCurrentHealthPoint; } // Initialize methods [Button("컴포넌트 초기화")] private void InitializeComponents() { _spriteRenderer = GetComponentInChildren(); _dashable = GetComponent(); _skillHandler = GetComponent(); } // Methods public void SetCurrentHealthPoint(int changedHealthPoint) { var newChangedHealthPoint = Mathf.Clamp(changedHealthPoint, 0, MaxHealthPoint); CurrentHealthPoint = newChangedHealthPoint; OnHealthChanged?.Invoke(newChangedHealthPoint); if (CurrentHealthPoint <= 2) { PostProcessingManager.Instance.LowHpVignette(); } else { PostProcessingManager.Instance.DefaultHpVignette(); } } public bool CanDamage() { if (IsInvincible) return false; var isDashing = _dashable?.IsDashing ?? false; var isActivatingSkill = _skillHandler?.IsActivatingSkill ?? false; return !isDashing && !isActivatingSkill; } public void TakeDamage(int damageAmount) { IsInvincible = true; var changeHp = Mathf.Max(CurrentHealthPoint - damageAmount, 0); SetCurrentHealthPoint(changeHp); AudioManager.Instance.PlaySfx("CombatPlayerAttacked"); // 죽었는지 체크 if (changeHp == 0f) { Die(); return; } if (_spriteRenderer.material.HasInt(_isHitHash)) { Utils.StartUniqueCoroutine(this, ref _flashWhiteCoroutine, FlashWhiteCoroutine()); } Utils.StartUniqueCoroutine(this, ref _damageIntervalCoroutine, Utils.CoolDownCoroutine(InvincibilityDuration, EndDamageIntervalCoroutine)); } public void TryTakeDamage(int damageAmount) { if (!CanDamage()) return; TakeDamage(damageAmount); } public void Die() { OnDead?.Invoke(); } private IEnumerator FlashWhiteCoroutine() { for (var i = 0; i < 5; i++) { _spriteRenderer.material.SetInt(_isHitHash, 1); yield return _flashWhiteWaitTime; _spriteRenderer.material.SetInt(_isHitHash, 0); yield return _flashWhiteWaitTime; } Utils.EndUniqueCoroutine(this, ref _flashWhiteCoroutine); } private void EndDamageIntervalCoroutine() { IsInvincible = false; Utils.EndUniqueCoroutine(this, ref _damageIntervalCoroutine); } public void ActivateInvincibility() => IsInvincible = true; public void DeactivateInvincibility() => IsInvincible = false; } }