299 lines
10 KiB
C#
299 lines
10 KiB
C#
using System;
|
|
using Unity.Netcode;
|
|
using UnityEngine;
|
|
|
|
namespace Northbound
|
|
{
|
|
/// <summary>
|
|
/// 건물 토대 - 플레이어가 상호작용하여 건물을 완성시킴
|
|
/// </summary>
|
|
public class BuildingFoundation : NetworkBehaviour, IInteractable, ITeamMember
|
|
{
|
|
[Header("Building Info")]
|
|
public BuildingData buildingData;
|
|
public Vector3Int gridPosition;
|
|
public int rotation;
|
|
|
|
[Header("Visual")]
|
|
public GameObject foundationVisual;
|
|
public GameObject progressBarPrefab;
|
|
|
|
// 현재 건설 진행도
|
|
private NetworkVariable<float> _currentProgress = new NetworkVariable<float>(
|
|
0f,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
// 건물 소유자
|
|
private NetworkVariable<ulong> _ownerId = new NetworkVariable<ulong>(
|
|
0,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
// 팀
|
|
private NetworkVariable<TeamType> _team = new NetworkVariable<TeamType>(
|
|
TeamType.Neutral,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
// 이벤트
|
|
public event Action<float, float> OnProgressChanged; // (current, max)
|
|
public event Action OnConstructionComplete;
|
|
|
|
private GameObject _progressBarInstance;
|
|
private float _lastInteractionTime;
|
|
private BoxCollider _collider;
|
|
|
|
public ulong OwnerId => _ownerId.Value;
|
|
|
|
public override void OnNetworkSpawn()
|
|
{
|
|
base.OnNetworkSpawn();
|
|
|
|
_currentProgress.OnValueChanged += OnProgressValueChanged;
|
|
|
|
// 진행 UI 생성
|
|
if (progressBarPrefab != null)
|
|
{
|
|
_progressBarInstance = Instantiate(progressBarPrefab, transform);
|
|
UpdateProgressBar();
|
|
}
|
|
}
|
|
|
|
public override void OnNetworkDespawn()
|
|
{
|
|
_currentProgress.OnValueChanged -= OnProgressValueChanged;
|
|
|
|
base.OnNetworkDespawn();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 토대 초기화
|
|
/// </summary>
|
|
public void Initialize(BuildingData data, Vector3Int pos, int rot, ulong ownerId, TeamType team)
|
|
{
|
|
if (!IsOwner)
|
|
{
|
|
InitializeServerRpc(data != null ? data.buildingName : "", pos.x, pos.y, pos.z, rot, ownerId, team);
|
|
return;
|
|
}
|
|
|
|
InitializeServerRpc(data != null ? data.buildingName : "", pos.x, pos.y, pos.z, rot, ownerId, team);
|
|
}
|
|
|
|
[ServerRpc]
|
|
private void InitializeServerRpc(string buildingName, int posX, int posY, int posZ, int rot, ulong ownerId, TeamType team)
|
|
{
|
|
buildingData = BuildingManager.Instance?.availableBuildings.Find(b => b != null && b.buildingName == buildingName);
|
|
gridPosition = new Vector3Int(posX, posY, posZ);
|
|
rotation = rot;
|
|
_ownerId.Value = ownerId;
|
|
_team.Value = team;
|
|
_currentProgress.Value = 0f;
|
|
|
|
// BuildingData의 크기를 기반으로 스케일 설정
|
|
Vector3 size = buildingData != null ? buildingData.GetSize(rot) : Vector3.one;
|
|
|
|
// foundationVisual의 스케일만 조정 (토대 자체의 pivot은 중앙에 유지)
|
|
if (foundationVisual != null)
|
|
{
|
|
// 토대 높이를 건물 크기에 맞게 조정 (높이는 얇게)
|
|
foundationVisual.transform.localScale = new Vector3(size.x, 0.2f, size.z);
|
|
foundationVisual.transform.localPosition = new Vector3(0, 0.1f, 0); // 바닥에서 약간 위
|
|
}
|
|
|
|
// BoxCollider 추가 및 크기 설정 (상호작용용)
|
|
_collider = GetComponent<BoxCollider>();
|
|
if (_collider == null)
|
|
{
|
|
_collider = gameObject.AddComponent<BoxCollider>();
|
|
}
|
|
|
|
// 상호작용 가능한 크기로 설정 (전체 건물 높이가 아니라 접근 가능한 크기)
|
|
_collider.size = new Vector3(size.x, 2f, size.z); // 높이를 2m로 설정하여 상호작용 가능
|
|
_collider.center = new Vector3(0, 1f, 0); // 중심을 1m 높이에 배치
|
|
_collider.isTrigger = false; // Trigger가 아닌 일반 Collider로 설정
|
|
|
|
Debug.Log($"<color=yellow>[BuildingFoundation] 토대 생성: {buildingData?.buildingName ?? "Building"}, 크기: {size}, 위치: {transform.position}, Collider: {_collider.size}, 소유자: {ownerId}, 팀: {TeamManager.GetTeamName(team)}</color>");
|
|
}
|
|
|
|
private void UpdateProgressBar()
|
|
{
|
|
if (buildingData == null || _progressBarInstance == null)
|
|
return;
|
|
|
|
float progress = buildingData.requiredWorkAmount > 0 ? _currentProgress.Value / buildingData.requiredWorkAmount : 1f;
|
|
|
|
// 간단한 progress bar update - 필요한 경우 BuildingProgressBar 컴포넌트 사용
|
|
var progressBarTransform = _progressBarInstance.transform;
|
|
progressBarTransform.localScale = new Vector3(progress, 1f, 1f);
|
|
}
|
|
|
|
private void OnProgressValueChanged(float previousValue, float newValue)
|
|
{
|
|
UpdateProgressBar();
|
|
float max = buildingData != null ? buildingData.requiredWorkAmount : 100f;
|
|
OnProgressChanged?.Invoke(newValue, max);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 토대의 그리드 경계 가져오기 (BuildingManager의 충돌 체크용)
|
|
/// </summary>
|
|
public Bounds GetGridBounds()
|
|
{
|
|
if (buildingData == null)
|
|
return new Bounds(transform.position, Vector3.one);
|
|
|
|
Vector3 size = buildingData.GetSize(rotation);
|
|
// 토대의 위치를 중심으로 건물이 차지할 공간 반환
|
|
return new Bounds(transform.position + Vector3.up * size.y * 0.5f, size);
|
|
}
|
|
|
|
public Bounds GetBounds()
|
|
{
|
|
return GetGridBounds();
|
|
}
|
|
|
|
#region IInteractable Implementation
|
|
|
|
public bool CanInteract(ulong playerId)
|
|
{
|
|
if (buildingData == null)
|
|
{
|
|
Debug.LogWarning($"[BuildingFoundation] buildingData is null");
|
|
return false;
|
|
}
|
|
|
|
// 이미 완성됨
|
|
if (_currentProgress.Value >= buildingData.requiredWorkAmount)
|
|
{
|
|
Debug.Log($"[BuildingFoundation] Already completed");
|
|
return false;
|
|
}
|
|
|
|
// 같은 팀만 건설 가능 - 플레이어의 팀을 가져와서 비교
|
|
TeamType playerTeam = GetPlayerTeam(playerId);
|
|
if (playerTeam != _team.Value)
|
|
{
|
|
Debug.LogWarning($"[BuildingFoundation] Wrong team: player={playerTeam}, foundation={_team.Value}");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void Interact(ulong playerId)
|
|
{
|
|
if (!IsServer || buildingData == null)
|
|
return;
|
|
|
|
if (!CanInteract(playerId))
|
|
return;
|
|
|
|
_lastInteractionTime = Time.time;
|
|
|
|
// 건설 진행
|
|
_currentProgress.Value += buildingData.workPerInteraction;
|
|
|
|
Debug.Log($"<color=yellow>[BuildingFoundation] {buildingData.buildingName} 건설 진행: {_currentProgress.Value}/{buildingData.requiredWorkAmount}</color>");
|
|
|
|
// 완료 체크
|
|
if (_currentProgress.Value >= buildingData.requiredWorkAmount)
|
|
{
|
|
CompleteConstruction(playerId);
|
|
}
|
|
}
|
|
|
|
private void CompleteConstruction(ulong playerId)
|
|
{
|
|
Debug.Log($"<color=green>[BuildingFoundation] {buildingData.buildingName} 건설 완료! 완성자: {playerId}</color>");
|
|
|
|
OnConstructionComplete?.Invoke();
|
|
|
|
// 토대 디스폰
|
|
if (IsServer && NetworkObject != null)
|
|
{
|
|
NetworkObject.Despawn(true);
|
|
}
|
|
|
|
// 상호작용 UI 제거
|
|
if (_progressBarInstance != null)
|
|
{
|
|
Destroy(_progressBarInstance);
|
|
_progressBarInstance = null;
|
|
}
|
|
}
|
|
|
|
private TeamType GetPlayerTeam(ulong playerId)
|
|
{
|
|
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(playerId, out NetworkObject playerObj))
|
|
{
|
|
var teamMember = playerObj.GetComponent<ITeamMember>();
|
|
if (teamMember != null)
|
|
{
|
|
return teamMember.GetTeam();
|
|
}
|
|
}
|
|
return TeamType.Neutral;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IInteractable Implementation - Getters
|
|
|
|
public string GetInteractionPrompt()
|
|
{
|
|
if (buildingData == null)
|
|
return "건설하기";
|
|
float workNeeded = buildingData.requiredWorkAmount - _currentProgress.Value;
|
|
float interactionsNeeded = Mathf.Ceil(workNeeded / buildingData.workPerInteraction);
|
|
return $"[{interactionsNeeded}] 건설하기";
|
|
}
|
|
|
|
public string GetInteractionAnimation()
|
|
{
|
|
if (buildingData != null && buildingData.constructionAnimationTrigger != null)
|
|
return buildingData.constructionAnimationTrigger;
|
|
return null;
|
|
}
|
|
|
|
public EquipmentData GetEquipmentData()
|
|
{
|
|
return buildingData != null ? buildingData.constructionEquipment : null;
|
|
}
|
|
|
|
public Transform GetTransform()
|
|
{
|
|
return transform;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ITeamMember Implementation
|
|
|
|
public TeamType GetTeam() => _team.Value;
|
|
|
|
public void SetTeam(TeamType team)
|
|
{
|
|
if (!IsOwner)
|
|
{
|
|
SetTeamServerRpc(team);
|
|
return;
|
|
}
|
|
|
|
SetTeamServerRpc(team);
|
|
}
|
|
|
|
[ServerRpc]
|
|
private void SetTeamServerRpc(TeamType team)
|
|
{
|
|
_team.Value = team;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|