using System; using Unity.Netcode; using UnityEngine; using UnityEngine.Events; [DisallowMultipleComponent] [RequireComponent(typeof(NetworkObject))] public sealed class NetworkBaseHealth : NetworkBehaviour { [SerializeField, Min(1)] private int maximumHealth = 10; [SerializeField] private UnityEvent healthChanged; [SerializeField] private UnityEvent depleted; private readonly NetworkVariable currentHealth = new( 0, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server); public int MaximumHealth => Mathf.Max(1, maximumHealth); public int CurrentHealth => currentHealth.Value; public bool IsDepleted => CurrentHealth <= 0; public event Action Changed; public event Action Depleted; private void OnValidate() => maximumHealth = Mathf.Max(1, maximumHealth); public override void OnNetworkSpawn() { currentHealth.OnValueChanged += HandleHealthChanged; if (IsServer) currentHealth.Value = MaximumHealth; ApplyState(currentHealth.Value, false); } public override void OnNetworkDespawn() { currentHealth.OnValueChanged -= HandleHealthChanged; } public bool ApplyDamage(int amount) { if (!IsServer || !IsSpawned || amount <= 0 || IsDepleted) return false; currentHealth.Value = Mathf.Max(0, CurrentHealth - amount); return true; } public void ResetHealth() { if (IsServer && IsSpawned) currentHealth.Value = MaximumHealth; } [ContextMenu("Debug/Apply One Damage (Server Play Mode)")] private void ApplyDebugDamage() { if (Application.isPlaying) ApplyDamage(1); } private void HandleHealthChanged(int previous, int current) => ApplyState(current, previous > 0 && current <= 0); private void ApplyState(int health, bool becameDepleted) { Changed?.Invoke(health); healthChanged?.Invoke(health); if (!becameDepleted) return; Depleted?.Invoke(); depleted?.Invoke(); } }