using System; using UnityEngine; using UnityEngine.Events; namespace MegaKoop.Game.Combat { [DisallowMultipleComponent] public class Health : MonoBehaviour, IDamageable { [SerializeField] private float maxHealth = 100f; [SerializeField] private Team team = Team.Neutral; [SerializeField] private bool ignoreFriendlyFire = true; [SerializeField] private bool destroyOnDeath = false; [SerializeField] private UnityEvent onHealthChanged; [SerializeField] private UnityEvent onDeath; public float MaxHealth => maxHealth; public float CurrentHealth { get; private set; } public Team Team => team; public bool IsAlive => CurrentHealth > 0f; public event Action NormalizedHealthChanged; private void Awake() { maxHealth = Mathf.Max(1f, maxHealth); CurrentHealth = maxHealth; onHealthChanged?.Invoke(CurrentHealth / MaxHealth); NormalizedHealthChanged?.Invoke(CurrentHealth / MaxHealth); } public void ApplyDamage(DamagePayload payload) { if (!IsAlive || payload.Amount <= 0f) { return; } if (ignoreFriendlyFire && team != Team.Neutral && payload.SourceTeam == team) { return; } CurrentHealth = Mathf.Max(0f, CurrentHealth - payload.Amount); float normalized = CurrentHealth / MaxHealth; onHealthChanged?.Invoke(normalized); NormalizedHealthChanged?.Invoke(normalized); if (CurrentHealth <= 0f) { HandleDeath(); } } public void Heal(float amount) { if (amount <= 0f || !IsAlive) { return; } CurrentHealth = Mathf.Min(MaxHealth, CurrentHealth + amount); float normalized = CurrentHealth / MaxHealth; onHealthChanged?.Invoke(normalized); NormalizedHealthChanged?.Invoke(normalized); } public void Revive(float overrideMaxHealth = -1f) { if (overrideMaxHealth > 0f) { maxHealth = Mathf.Max(1f, overrideMaxHealth); } CurrentHealth = maxHealth; float normalized = CurrentHealth / MaxHealth; onHealthChanged?.Invoke(normalized); NormalizedHealthChanged?.Invoke(normalized); } private void HandleDeath() { onDeath?.Invoke(); if (destroyOnDeath) { Destroy(gameObject); } } private void OnValidate() { maxHealth = Mathf.Max(1f, maxHealth); } public void ForceSetNormalizedHealth(float normalized) { normalized = Mathf.Clamp01(normalized); bool wasAlive = IsAlive; CurrentHealth = normalized * MaxHealth; onHealthChanged?.Invoke(normalized); NormalizedHealthChanged?.Invoke(normalized); if (CurrentHealth <= 0f && wasAlive) { HandleDeath(); } } } }