using System; using UnityEngine; using Unity.Netcode; using Colosseum.Stats; using Colosseum.Combat; namespace Colosseum.Player { /// /// 플레이어 네트워크 상태 관리 (HP, MP 등) /// public class PlayerNetworkController : NetworkBehaviour, IDamageable { [Header("References")] [Tooltip("CharacterStats 컴포넌트 (없으면 자동 검색)")] [SerializeField] private CharacterStats characterStats; // 네트워크 동기화 변수 private NetworkVariable currentHealth = new NetworkVariable(100f); private NetworkVariable currentMana = new NetworkVariable(50f); public float Health => currentHealth.Value; public float Mana => currentMana.Value; public float MaxHealth => characterStats != null ? characterStats.MaxHealth : 100f; public float MaxMana => characterStats != null ? characterStats.MaxMana : 50f; public CharacterStats Stats => characterStats; // 체력/마나 변경 이벤트 public event Action OnHealthChanged; // (oldValue, newValue) public event Action OnManaChanged; // (oldValue, newValue) // IDamageable 구현 public float CurrentHealth => currentHealth.Value; public bool IsDead => currentHealth.Value <= 0f; public override void OnNetworkSpawn() { // CharacterStats 참조 확인 if (characterStats == null) { characterStats = GetComponent(); } // 네트워크 변수 변경 콜백 등록 currentHealth.OnValueChanged += HandleHealthChanged; currentMana.OnValueChanged += HandleManaChanged; // 초기화 if (IsServer) { currentHealth.Value = MaxHealth; currentMana.Value = MaxMana; } } public override void OnNetworkDespawn() { // 콜백 해제 currentHealth.OnValueChanged -= HandleHealthChanged; currentMana.OnValueChanged -= HandleManaChanged; } private void HandleHealthChanged(float oldValue, float newValue) { OnHealthChanged?.Invoke(oldValue, newValue); } private void HandleManaChanged(float oldValue, float newValue) { OnManaChanged?.Invoke(oldValue, newValue); } /// /// 대미지 적용 (서버에서만 실행) /// [Rpc(SendTo.Server)] public void TakeDamageRpc(float damage) { currentHealth.Value = Mathf.Max(0f, currentHealth.Value - damage); if (currentHealth.Value <= 0f) { HandleDeath(); } } /// /// 마나 소모 (서버에서만 실행) /// [Rpc(SendTo.Server)] public void UseManaRpc(float amount) { currentMana.Value = Mathf.Max(0f, currentMana.Value - amount); } /// /// 체력 회복 (서버에서만 실행) /// [Rpc(SendTo.Server)] public void RestoreHealthRpc(float amount) { currentHealth.Value = Mathf.Min(MaxHealth, currentHealth.Value + amount); } /// /// 마나 회복 (서버에서만 실행) /// [Rpc(SendTo.Server)] public void RestoreManaRpc(float amount) { currentMana.Value = Mathf.Min(MaxMana, currentMana.Value + amount); } private void HandleDeath() { // TODO: 사망 처리 로직 Debug.Log($"Player {OwnerClientId} died!"); } #region IDamageable /// /// 대미지 적용 (서버에서만 호출) /// public float TakeDamage(float damage, object source = null) { if (!IsServer) return 0f; float actualDamage = Mathf.Min(damage, currentHealth.Value); currentHealth.Value = Mathf.Max(0f, currentHealth.Value - damage); if (currentHealth.Value <= 0f) { HandleDeath(); } return actualDamage; } /// /// 체력 회복 (서버에서만 호출) /// public float Heal(float amount) { if (!IsServer) return 0f; float actualHeal = Mathf.Min(amount, MaxHealth - currentHealth.Value); currentHealth.Value = Mathf.Min(MaxHealth, currentHealth.Value + amount); return actualHeal; } #endregion } }