using System.Collections; using BlueWater.Audios; using BlueWater.Interfaces; using BlueWater.Utility; using Sirenix.OdinInspector; using UnityEngine; namespace BlueWater.Players { public class PlayerHealthPoint : MonoBehaviour, IDamageable { // Components 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; } [SerializeField] private bool _isShaking; [SerializeField, ShowIf("@_isShaking")] private float _shakingPower = 0.5f; [SerializeField, ShowIf("@_isShaking")] private float _shakingDuration = 0.1f; [SerializeField] private string attackedSfxName = "CombatPlayerAttacked"; [SerializeField] private string heartRecoverySfxName; private Material _materialInstance; private WaitForSeconds _flashWhiteWaitTime; private Coroutine _flashWhiteCoroutine; private Coroutine _damageIntervalCoroutine; // Hashes private static readonly int IsHitHash = Shader.PropertyToID("_IsHit"); // Unity events private void Awake() { InitializeComponents(); } private void Start() { _flashWhiteWaitTime = new WaitForSeconds(InvincibilityDuration * 0.1f); SetCurrentHealthPoint(MaxHealthPoint); } // Initialize methods [Button("컴포넌트 초기화")] private void InitializeComponents() { _dashable = GetComponent(); _skillHandler = GetComponent(); } // Methods public void SetMaxHealthPoint(int changedHealthPoint) { var previousMaxHealthPoint = MaxHealthPoint; var newChangedHealthPoint = Mathf.Clamp(changedHealthPoint, 0, 10); MaxHealthPoint = newChangedHealthPoint; EventManager.InvokeMaxHealthChanged(previousMaxHealthPoint, newChangedHealthPoint); } public void SetCurrentHealthPoint(int changedHealthPoint) { int newChangedHealthPoint = Mathf.Clamp(changedHealthPoint, 0, MaxHealthPoint); int addedHealthPoint = newChangedHealthPoint - CurrentHealthPoint; CurrentHealthPoint = newChangedHealthPoint; if (addedHealthPoint > 0) { if (!string.IsNullOrEmpty(heartRecoverySfxName)) { AudioManager.Instance.PlaySfx(heartRecoverySfxName); } } EventManager.InvokeHealthChanged(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; } [Button("데미지 테스트")] public void TakeDamage(int damageAmount) { AudioManager.Instance.PlaySfx(attackedSfxName); IsInvincible = true; var changeHp = Mathf.Max(CurrentHealthPoint - damageAmount, 0); SetCurrentHealthPoint(changeHp); if (_isShaking) { VisualFeedbackManager.Instance.CameraShake(TycoonCameraManager.Instance.BaseCamera, _shakingPower, _shakingDuration); } // 죽었는지 체크 if (changeHp == 0f) { Die(); return; } if (_materialInstance.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() { EventManager.InvokeDead(); } private IEnumerator FlashWhiteCoroutine() { for (var i = 0; i < 5; i++) { _materialInstance.SetInt(IsHitHash, 1); yield return _flashWhiteWaitTime; _materialInstance.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; public void SetMaterialInstance(Material materialInstance) => _materialInstance = materialInstance; } }