using System;
using Unity.Netcode;
using UnityEngine;
namespace Northbound
{
///
/// 건물 토대 - 플레이어가 상호작용하여 건물을 완성시킴
///
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 _currentProgress = new NetworkVariable(
0f,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Server
);
// 건물 소유자
private NetworkVariable _ownerId = new NetworkVariable(
0,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Server
);
// 팀
private NetworkVariable _team = new NetworkVariable(
TeamType.Neutral,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Server
);
// 이벤트
public event Action 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();
}
///
/// 토대 초기화
///
public void Initialize(BuildingData data, Vector3Int pos, int rot, ulong ownerId, TeamType team)
{
if (!IsServer) return;
buildingData = data;
gridPosition = pos;
rotation = rot;
_ownerId.Value = ownerId;
_team.Value = team;
_currentProgress.Value = 0f;
// BuildingData의 크기를 기반으로 스케일 설정
Vector3 size = data.GetSize(rot);
// 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();
if (_collider == null)
{
_collider = gameObject.AddComponent();
}
// 상호작용 가능한 크기로 설정 (전체 건물 높이가 아닌 접근 가능한 크기)
_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($"[BuildingFoundation] 토대 생성: {data.buildingName}, 크기: {size}, 위치: {transform.position}, Collider: {_collider.size}, 소유자: {ownerId}, 팀: {team}");
}
///
/// 토대의 그리드 경계 가져오기 (BuildingManager의 충돌 체크용)
///
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);
}
#region IInteractable Implementation
public bool CanInteract(ulong playerId)
{
if (buildingData == null)
{
Debug.LogWarning($"[BuildingFoundation] buildingData is null");
return false;
}
// 쿨다운 체크
if (Time.time - _lastInteractionTime < buildingData.interactionCooldown)
{
Debug.Log($"[BuildingFoundation] Cooldown active: {Time.time - _lastInteractionTime}/{buildingData.interactionCooldown}");
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;
}
Debug.Log($"[BuildingFoundation] CanInteract = true");
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($"[BuildingFoundation] 건설 진행: {_currentProgress.Value}/{buildingData.requiredWorkAmount} ({(_currentProgress.Value / buildingData.requiredWorkAmount * 100f):F1}%)");
// 완성 체크
if (_currentProgress.Value >= buildingData.requiredWorkAmount)
{
CompleteConstruction();
}
}
public string GetInteractionPrompt()
{
if (buildingData == null)
return "[E] 건설하기";
float percentage = (_currentProgress.Value / buildingData.requiredWorkAmount) * 100f;
return $"[E] 건설하기 ({percentage:F0}%)";
}
public string GetInteractionAnimation()
{
// BuildingData에서 애니메이션 트리거 가져오기
if (buildingData != null && !string.IsNullOrEmpty(buildingData.constructionAnimationTrigger))
{
return buildingData.constructionAnimationTrigger;
}
// 기본값: 빈 문자열 (애니메이션 없음)
return "";
}
public InteractionEquipmentData GetEquipmentData()
{
// BuildingData에 건설 도구가 정의되어 있으면 반환
if (buildingData != null && buildingData.constructionEquipment != null)
{
return buildingData.constructionEquipment;
}
return null; // 특별한 도구 불필요
}
public Transform GetTransform()
{
return transform;
}
#endregion
#region ITeamMember Implementation
public TeamType GetTeam()
{
return _team.Value;
}
public void SetTeam(TeamType team)
{
if (!IsServer) return;
_team.Value = team;
}
#endregion
///
/// 플레이어의 팀 가져오기
///
private TeamType GetPlayerTeam(ulong playerId)
{
// 플레이어의 NetworkObject 찾기
if (NetworkManager.Singleton != null && NetworkManager.Singleton.SpawnManager != null)
{
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(playerId, out NetworkObject playerNetObj))
{
var teamMember = playerNetObj.GetComponent();
if (teamMember != null)
{
return teamMember.GetTeam();
}
}
}
// 기본값: 플레이어 팀
return TeamType.Player;
}
private void CompleteConstruction()
{
if (!IsServer) return;
Debug.Log($"[BuildingFoundation] 건물 완성! {buildingData.buildingName}");
OnConstructionComplete?.Invoke();
// BuildingManager에서 토대 제거
var buildingManager = BuildingManager.Instance;
if (buildingManager != null)
{
buildingManager.RemoveFoundation(this);
}
// 완성된 건물 생성
SpawnCompletedBuilding();
// 토대 제거
if (NetworkObject != null)
{
NetworkObject.Despawn(true);
}
}
private void SpawnCompletedBuilding()
{
if (!IsServer || buildingData == null || buildingData.prefab == null)
return;
// BuildingManager를 통해 건물 생성
var buildingManager = BuildingManager.Instance;
if (buildingManager != null)
{
buildingManager.SpawnCompletedBuildingServerRpc(
buildingData.name,
gridPosition,
rotation,
_ownerId.Value,
_team.Value
);
}
else
{
Debug.LogError("[BuildingFoundation] BuildingManager를 찾을 수 없습니다!");
}
}
private void OnProgressValueChanged(float oldValue, float newValue)
{
if (buildingData != null)
{
OnProgressChanged?.Invoke(newValue, buildingData.requiredWorkAmount);
}
UpdateProgressBar();
}
private void UpdateProgressBar()
{
if (_progressBarInstance == null || buildingData == null) return;
// 진행바 UI 업데이트 (BuildingHealthBar와 유사한 구조 사용 가능)
var progressBar = _progressBarInstance.GetComponent();
if (progressBar != null)
{
// BuildingHealthBar를 재사용하여 진행도 표시
progressBar.UpdateHealth((int)_currentProgress.Value, (int)buildingData.requiredWorkAmount);
}
}
private void OnDrawGizmos()
{
if (buildingData == null) return;
// 건물 경계 표시 (노란색)
Gizmos.color = Color.yellow;
Vector3 size = buildingData.GetSize(rotation);
Gizmos.DrawWireCube(transform.position + Vector3.up * size.y * 0.5f, size);
// Collider 경계 표시 (초록색)
if (_collider != null)
{
Gizmos.color = Color.green;
Gizmos.DrawWireCube(transform.position + _collider.center, _collider.size);
}
// 상호작용 가능 여부 표시
if (_currentProgress.Value < (buildingData?.requiredWorkAmount ?? 100f))
{
Gizmos.color = Color.cyan;
Gizmos.DrawSphere(transform.position + Vector3.up, 0.3f);
}
}
}
}