321 lines
9.7 KiB
C#
321 lines
9.7 KiB
C#
using Unity.Netcode;
|
|
using UnityEngine;
|
|
|
|
namespace Northbound
|
|
{
|
|
/// <summary>
|
|
/// 플레이어가 자원을 건내받아 게임의 전역 자원으로 관리하는 중앙 허브
|
|
/// </summary>
|
|
public class Core : NetworkBehaviour, IInteractable, IDamageable, ITeamMember
|
|
{
|
|
[Header("Core Settings")]
|
|
public int maxStorageCapacity = 1000; // 코어의 최대 저장 용량
|
|
public bool unlimitedStorage = false; // 무제한 저장소
|
|
|
|
[Header("Health")]
|
|
public int maxHealth = 1000;
|
|
public GameObject damageEffectPrefab;
|
|
public GameObject destroyEffectPrefab;
|
|
|
|
[Header("Deposit Settings")]
|
|
public bool depositAll = true; // true: 전부 건네기, false: 일부만 건네기
|
|
public int depositAmountPerInteraction = 10; // depositAll이 false일 때 한 번에 건네는 양
|
|
|
|
[Header("Animation")]
|
|
public string interactionAnimationTrigger = "Deposit"; // 플레이어 애니메이션 트리거
|
|
|
|
[Header("Equipment")]
|
|
public EquipmentData equipmentData = null; // 도구 필요 없음
|
|
|
|
[Header("Visual")]
|
|
public GameObject depositEffectPrefab;
|
|
public Transform effectSpawnPoint;
|
|
|
|
private NetworkVariable<int> _totalResources = new NetworkVariable<int>(
|
|
0,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
private NetworkVariable<int> _currentHealth = new NetworkVariable<int>(
|
|
0,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
public int TotalResources => _totalResources.Value;
|
|
public int MaxStorageCapacity => maxStorageCapacity;
|
|
public int CurrentHealth => _currentHealth.Value;
|
|
public int MaxHealth => maxHealth;
|
|
|
|
public override void OnNetworkSpawn()
|
|
{
|
|
if (IsServer)
|
|
{
|
|
_totalResources.Value = 0;
|
|
_currentHealth.Value = maxHealth;
|
|
}
|
|
|
|
_currentHealth.OnValueChanged += OnHealthChanged;
|
|
}
|
|
|
|
public override void OnNetworkDespawn()
|
|
{
|
|
_currentHealth.OnValueChanged -= OnHealthChanged;
|
|
}
|
|
|
|
private void OnHealthChanged(int previousValue, int newValue)
|
|
{
|
|
}
|
|
|
|
#region ITeamMember Implementation
|
|
|
|
public TeamType GetTeam()
|
|
{
|
|
return TeamType.Player; // 코어는 플레이어 팀
|
|
}
|
|
|
|
public void SetTeam(TeamType team)
|
|
{
|
|
// 코어의 팀은 변경할 수 없음 (항상 플레이어 팀)
|
|
Debug.LogWarning("[Core] 코어의 팀은 변경할 수 없습니다.");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDamageable Implementation
|
|
|
|
public void TakeDamage(int damage, ulong attackerId)
|
|
{
|
|
if (!IsServer) return;
|
|
if (_currentHealth.Value <= 0) return;
|
|
|
|
int actualDamage = Mathf.Min(damage, _currentHealth.Value);
|
|
_currentHealth.Value -= actualDamage;
|
|
|
|
// 데미지 이펙트
|
|
ShowDamageEffectClientRpc();
|
|
|
|
// 체력이 0이 되면 게임 오버
|
|
if (_currentHealth.Value <= 0)
|
|
{
|
|
OnCoreDestroyed();
|
|
}
|
|
}
|
|
|
|
private void OnCoreDestroyed()
|
|
{
|
|
if (!IsServer) return;
|
|
|
|
// 파괴 이펙트
|
|
ShowDestroyEffectClientRpc();
|
|
|
|
// 게임 오버 로직 (추후 구현)
|
|
// GameManager.Instance?.OnGameOver();
|
|
}
|
|
|
|
[Rpc(SendTo.ClientsAndHost)]
|
|
private void ShowDamageEffectClientRpc()
|
|
{
|
|
if (damageEffectPrefab != null)
|
|
{
|
|
GameObject effect = Instantiate(damageEffectPrefab, transform.position + Vector3.up * 2f, Quaternion.identity);
|
|
Destroy(effect, 2f);
|
|
}
|
|
}
|
|
|
|
[Rpc(SendTo.ClientsAndHost)]
|
|
private void ShowDestroyEffectClientRpc()
|
|
{
|
|
if (destroyEffectPrefab != null)
|
|
{
|
|
GameObject effect = Instantiate(destroyEffectPrefab, transform.position, Quaternion.identity);
|
|
Destroy(effect, 5f);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Resource Management
|
|
|
|
/// <summary>
|
|
/// 자원을 소비할 수 있는지 확인
|
|
/// </summary>
|
|
public bool CanConsumeResource(int amount)
|
|
{
|
|
return _totalResources.Value >= amount;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 자원 소비 (서버에서만)
|
|
/// </summary>
|
|
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
|
public void ConsumeResourceServerRpc(int amount)
|
|
{
|
|
if (!CanConsumeResource(amount))
|
|
{
|
|
Debug.LogWarning($"[Core] 자원이 부족합니다. 필요: {amount}, 보유: {_totalResources.Value}");
|
|
return;
|
|
}
|
|
|
|
int previousAmount = _totalResources.Value;
|
|
_totalResources.Value -= amount;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 자원 추가 (서버에서만)
|
|
/// </summary>
|
|
public void AddResource(int amount)
|
|
{
|
|
if (!IsServer) return;
|
|
|
|
if (!unlimitedStorage)
|
|
{
|
|
int availableSpace = maxStorageCapacity - _totalResources.Value;
|
|
amount = Mathf.Min(amount, availableSpace);
|
|
}
|
|
|
|
if (amount > 0)
|
|
{
|
|
_totalResources.Value += amount;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IInteractable Implementation
|
|
|
|
public bool CanInteract(ulong playerId)
|
|
{
|
|
// 저장소가 가득 찼는지 확인 (무제한이 아닐 때)
|
|
if (!unlimitedStorage && _totalResources.Value >= maxStorageCapacity)
|
|
return false;
|
|
|
|
// 플레이어가 자원을 가지고 있는지 확인
|
|
if (NetworkManager.Singleton != null &&
|
|
NetworkManager.Singleton.ConnectedClients.TryGetValue(playerId, out var client))
|
|
{
|
|
if (client.PlayerObject != null)
|
|
{
|
|
var playerInventory = client.PlayerObject.GetComponent<PlayerResourceInventory>();
|
|
if (playerInventory != null)
|
|
{
|
|
// 플레이어가 자원을 가지고 있어야 함
|
|
return playerInventory.CurrentResourceAmount > 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void Interact(ulong playerId)
|
|
{
|
|
if (!CanInteract(playerId))
|
|
return;
|
|
|
|
DepositResourceServerRpc(playerId);
|
|
}
|
|
|
|
public string GetInteractionPrompt()
|
|
{
|
|
if (unlimitedStorage)
|
|
{
|
|
return "[E] Deposit (No Limit)";
|
|
}
|
|
else
|
|
{
|
|
return $"[E] Deposit ({_totalResources.Value}/{maxStorageCapacity})";
|
|
}
|
|
}
|
|
|
|
public string GetInteractionAnimation()
|
|
{
|
|
return interactionAnimationTrigger;
|
|
}
|
|
|
|
public EquipmentData GetEquipmentData()
|
|
{
|
|
return equipmentData;
|
|
}
|
|
|
|
public Transform GetTransform()
|
|
{
|
|
return transform;
|
|
}
|
|
|
|
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
|
private void DepositResourceServerRpc(ulong playerId)
|
|
{
|
|
if (!CanInteract(playerId))
|
|
return;
|
|
|
|
var resourceManager = ServerResourceManager.Instance;
|
|
if (resourceManager == null)
|
|
{
|
|
Debug.LogWarning("ServerResourceManager 인스턴스를 찾을 수 없습니다.");
|
|
return;
|
|
}
|
|
|
|
int playerResourceAmount = resourceManager.GetPlayerResourceAmount(playerId);
|
|
if (playerResourceAmount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int depositAmount;
|
|
|
|
if (depositAll)
|
|
{
|
|
depositAmount = playerResourceAmount;
|
|
}
|
|
else
|
|
{
|
|
depositAmount = Mathf.Min(depositAmountPerInteraction, playerResourceAmount);
|
|
}
|
|
|
|
if (!unlimitedStorage)
|
|
{
|
|
int availableSpace = maxStorageCapacity - _totalResources.Value;
|
|
depositAmount = Mathf.Min(depositAmount, availableSpace);
|
|
}
|
|
|
|
if (depositAmount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
resourceManager.RemoveResource(playerId, depositAmount);
|
|
UpdatePlayerResourcesClientRpc(playerId);
|
|
_totalResources.Value += depositAmount;
|
|
|
|
ShowDepositEffectClientRpc();
|
|
}
|
|
|
|
[Rpc(SendTo.ClientsAndHost)]
|
|
private void UpdatePlayerResourcesClientRpc(ulong playerId)
|
|
{
|
|
var playerObject = NetworkManager.Singleton.ConnectedClients[playerId].PlayerObject;
|
|
if (playerObject != null)
|
|
{
|
|
var playerInventory = playerObject.GetComponent<PlayerResourceInventory>();
|
|
if (playerInventory != null)
|
|
{
|
|
playerInventory.RequestResourceUpdateServerRpc();
|
|
}
|
|
}
|
|
}
|
|
|
|
[Rpc(SendTo.ClientsAndHost)]
|
|
private void ShowDepositEffectClientRpc()
|
|
{
|
|
if (depositEffectPrefab != null && effectSpawnPoint != null)
|
|
{
|
|
GameObject effect = Instantiate(depositEffectPrefab, effectSpawnPoint.position, effectSpawnPoint.rotation);
|
|
Destroy(effect, 2f);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |