396 lines
12 KiB
C#
396 lines
12 KiB
C#
using Unity.Netcode;
|
|
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Northbound
|
|
{
|
|
/// <summary>
|
|
/// 상호작용 대상 - 자원 채집
|
|
/// </summary>
|
|
public class Resource : NetworkBehaviour, IInteractable
|
|
{
|
|
[Header("Resource Settings")]
|
|
public int maxResources = 100;
|
|
public int resourcesPerGathering = 10;
|
|
public float gatheringCooldown = 2f;
|
|
public string resourceName = "광석";
|
|
|
|
[Header("Resource Recharge")]
|
|
public float rechargeInterval = 5f; // 충전 주기 (초)
|
|
public int rechargeAmount = 10; // 주기당 충전량
|
|
|
|
[Header("Quality (Runtime)")]
|
|
[SerializeField] private NetworkVariable<float> _qualityPercentage = new NetworkVariable<float>(
|
|
0f,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
[Tooltip("품질 보정율 (-30% ~ +30%)")]
|
|
[SerializeField] private float _displayQuality = 0f;
|
|
|
|
[Tooltip("품질 적용 후 최대 자원량")]
|
|
[SerializeField] private int _displayMaxResources = 100;
|
|
|
|
[Tooltip("품질 적용 후 충전량")]
|
|
[SerializeField] private int _displayRechargeAmount = 10;
|
|
|
|
private bool _isQualityInitialized = false;
|
|
|
|
[Header("Animation")]
|
|
public string interactionAnimationTrigger = "Mining"; // 플레이어 애니메이션 트리거
|
|
|
|
[Header("Equipment")]
|
|
public EquipmentData equipmentData = new EquipmentData
|
|
{
|
|
socketName = "RightHand",
|
|
equipmentPrefab = null, // Inspector에서 곡괭이 프리팹 할당
|
|
attachOnStart = true,
|
|
detachOnEnd = true
|
|
};
|
|
|
|
[Header("Visual")]
|
|
public GameObject gatheringEffectPrefab;
|
|
public Transform effectSpawnPoint;
|
|
|
|
[Header("Worker Assignment")]
|
|
public bool allowWorkerAssignment = true;
|
|
|
|
[Header("Multi-worker")]
|
|
public bool allowMultipleWorkers = false; // 한 자원에 여러 워커 허용 여부
|
|
|
|
public bool HasResourcesAvailable()
|
|
{
|
|
return _currentResources.Value > 0;
|
|
}
|
|
|
|
public bool CanWorkerMineResource(ulong workerId)
|
|
{
|
|
if (!HasResourcesAvailable())
|
|
return false;
|
|
|
|
if (allowMultipleWorkers)
|
|
return true;
|
|
|
|
if (_currentWorkerId.Value == ulong.MaxValue)
|
|
return true;
|
|
|
|
return _currentWorkerId.Value == workerId;
|
|
}
|
|
|
|
public int TakeResourcesForWorker(int amount, ulong workerId)
|
|
{
|
|
if (!IsServer) return 0;
|
|
|
|
if (!allowMultipleWorkers)
|
|
{
|
|
if (_currentWorkerId.Value != ulong.MaxValue && _currentWorkerId.Value != workerId)
|
|
return 0;
|
|
}
|
|
|
|
int availableResources = _currentResources.Value;
|
|
int actualAmount = Mathf.Min(amount, availableResources);
|
|
|
|
if (actualAmount <= 0)
|
|
return 0;
|
|
|
|
_currentResources.Value -= actualAmount;
|
|
_lastGatheringTime = Time.time;
|
|
|
|
if (!allowMultipleWorkers && actualAmount > 0)
|
|
{
|
|
_currentWorkerId.Value = workerId;
|
|
}
|
|
|
|
if (_currentResources.Value <= 0 && !allowMultipleWorkers)
|
|
{
|
|
_currentWorkerId.Value = ulong.MaxValue;
|
|
}
|
|
|
|
ShowGatheringEffectClientRpc();
|
|
|
|
return actualAmount;
|
|
}
|
|
|
|
private NetworkVariable<int> _currentResources = new NetworkVariable<int>(
|
|
0,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
private NetworkVariable<ulong> _currentWorkerId = new NetworkVariable<ulong>(
|
|
ulong.MaxValue,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
private float _lastGatheringTime;
|
|
private float _lastRechargeTime;
|
|
|
|
public float QualityPercentage => _qualityPercentage.Value;
|
|
|
|
public int ActualMaxResources
|
|
{
|
|
get
|
|
{
|
|
float multiplier = 1f + (_qualityPercentage.Value / 100f);
|
|
return Mathf.RoundToInt(maxResources * multiplier);
|
|
}
|
|
}
|
|
|
|
public int ActualRechargeAmount
|
|
{
|
|
get
|
|
{
|
|
float multiplier = 1f + (_qualityPercentage.Value / 100f);
|
|
return Mathf.RoundToInt(rechargeAmount * multiplier);
|
|
}
|
|
}
|
|
|
|
public override void OnNetworkSpawn()
|
|
{
|
|
_qualityPercentage.OnValueChanged += OnQualityChanged;
|
|
|
|
if (IsServer)
|
|
{
|
|
if (!_isQualityInitialized)
|
|
{
|
|
_qualityPercentage.Value = Random.Range(-30f, 30f);
|
|
}
|
|
|
|
_currentResources.Value = ActualMaxResources;
|
|
_lastRechargeTime = Time.time;
|
|
_lastGatheringTime = Time.time - gatheringCooldown;
|
|
}
|
|
|
|
_displayQuality = _qualityPercentage.Value;
|
|
UpdateDisplayValues();
|
|
}
|
|
|
|
public void InitializeQuality(float qualityPercentage)
|
|
{
|
|
if (IsServer)
|
|
{
|
|
_qualityPercentage.Value = qualityPercentage;
|
|
_displayQuality = qualityPercentage;
|
|
_isQualityInitialized = true;
|
|
}
|
|
else if (IsClient)
|
|
{
|
|
_displayQuality = qualityPercentage;
|
|
UpdateDisplayValues();
|
|
}
|
|
}
|
|
|
|
public override void OnNetworkDespawn()
|
|
{
|
|
_qualityPercentage.OnValueChanged -= OnQualityChanged;
|
|
}
|
|
|
|
private void OnQualityChanged(float previous, float current)
|
|
{
|
|
_displayQuality = current;
|
|
UpdateDisplayValues();
|
|
}
|
|
|
|
private void UpdateDisplayValues()
|
|
{
|
|
if (IsClient || IsServer)
|
|
{
|
|
_displayMaxResources = ActualMaxResources;
|
|
_displayRechargeAmount = ActualRechargeAmount;
|
|
}
|
|
else
|
|
{
|
|
_displayMaxResources = maxResources;
|
|
_displayRechargeAmount = rechargeAmount;
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!IsServer)
|
|
return;
|
|
|
|
// 자원 충전 로직
|
|
if (Time.time - _lastRechargeTime >= rechargeInterval)
|
|
{
|
|
if (_currentResources.Value < ActualMaxResources)
|
|
{
|
|
int rechargeAmountToAdd = Mathf.Min(ActualRechargeAmount, ActualMaxResources - _currentResources.Value);
|
|
_currentResources.Value += rechargeAmountToAdd;
|
|
|
|
// Debug.Log($"{resourceName} {rechargeAmountToAdd} 충전됨. 현재: {_currentResources.Value}/{ActualMaxResources}");
|
|
}
|
|
|
|
_lastRechargeTime = Time.time;
|
|
}
|
|
}
|
|
|
|
public bool CanInteract(ulong playerId)
|
|
{
|
|
if (_currentResources.Value <= 0)
|
|
return false;
|
|
|
|
if (Time.time - _lastGatheringTime < gatheringCooldown)
|
|
return false;
|
|
|
|
var resourceManager = ServerResourceManager.Instance;
|
|
if (resourceManager != null)
|
|
{
|
|
if (resourceManager.GetAvailableSpace(playerId) <= 0)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void Interact(ulong playerId)
|
|
{
|
|
AssignOrGatherResourceServerRpc(playerId, NetworkObject.NetworkObjectId);
|
|
}
|
|
|
|
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
|
private void AssignOrGatherResourceServerRpc(ulong playerId, ulong resourceId)
|
|
{
|
|
|
|
bool workerAssigned = false;
|
|
|
|
if (allowWorkerAssignment)
|
|
{
|
|
// Find worker owned by player in Following state (server-side, not client-side)
|
|
Worker assignedWorker = FindWorkerForPlayer(playerId);
|
|
|
|
if (assignedWorker != null)
|
|
{
|
|
if ((int)assignedWorker.CurrentState == 1) // 1 = Following
|
|
{
|
|
assignedWorker.AssignMiningTargetServerRpc(resourceId);
|
|
workerAssigned = true;
|
|
|
|
ShowGatheringEffectClientRpc();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!workerAssigned)
|
|
{
|
|
if (!CanInteract(playerId))
|
|
{
|
|
return;
|
|
}
|
|
|
|
GatherResource(playerId);
|
|
}
|
|
}
|
|
|
|
private void GatherResource(ulong playerId)
|
|
{
|
|
if (!CanInteract(playerId))
|
|
return;
|
|
|
|
var resourceManager = ServerResourceManager.Instance;
|
|
if (resourceManager == null)
|
|
return;
|
|
|
|
int playerAvailableSpace = resourceManager.GetAvailableSpace(playerId);
|
|
|
|
int gatheredAmount = Mathf.Min(
|
|
resourcesPerGathering,
|
|
_currentResources.Value,
|
|
playerAvailableSpace
|
|
);
|
|
|
|
if (gatheredAmount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_currentResources.Value -= gatheredAmount;
|
|
_lastGatheringTime = Time.time;
|
|
|
|
resourceManager.AddResource(playerId, gatheredAmount);
|
|
UpdatePlayerResourcesClientRpc(playerId);
|
|
|
|
ShowGatheringEffectClientRpc();
|
|
}
|
|
|
|
[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.NotServer)]
|
|
private void ShowGatheringEffectClientRpc()
|
|
{
|
|
if (gatheringEffectPrefab != null && effectSpawnPoint != null)
|
|
{
|
|
GameObject effect = Instantiate(gatheringEffectPrefab, effectSpawnPoint.position, effectSpawnPoint.rotation);
|
|
Destroy(effect, 2f);
|
|
}
|
|
}
|
|
|
|
public string GetInteractionPrompt()
|
|
{
|
|
if (_currentResources.Value <= 0)
|
|
return "Recharging...";
|
|
|
|
return $"[E] {resourceName} Mining ({_currentResources.Value}/{ActualMaxResources})";
|
|
}
|
|
|
|
public string GetInteractionAnimation()
|
|
{
|
|
return interactionAnimationTrigger;
|
|
}
|
|
|
|
public EquipmentData GetEquipmentData()
|
|
{
|
|
return equipmentData;
|
|
}
|
|
|
|
public Transform GetTransform()
|
|
{
|
|
return transform;
|
|
}
|
|
|
|
private Worker FindWorkerForPlayer(ulong playerId)
|
|
{
|
|
if (NetworkManager.Singleton == null || NetworkManager.Singleton.SpawnManager == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var spawnedObjects = NetworkManager.Singleton.SpawnManager.SpawnedObjects;
|
|
int workersFound = 0;
|
|
|
|
foreach (var kvp in spawnedObjects)
|
|
{
|
|
var networkObj = kvp.Value;
|
|
var worker = networkObj.GetComponent<Worker>();
|
|
|
|
if (worker != null)
|
|
{
|
|
workersFound++;
|
|
|
|
// Use worker's internal OwnerPlayerId, NOT NetworkObject.OwnerClientId!
|
|
if (worker.OwnerPlayerId == playerId && (int)worker.CurrentState == 1) // 1 = Following
|
|
{
|
|
return worker;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|