Compare commits

...

2 Commits

2 changed files with 381 additions and 1 deletions

View File

@@ -5,7 +5,7 @@ using Northbound.Data;
namespace Northbound
{
public class Building : NetworkBehaviour, IDamageable, IVisionProvider, ITeamMember, IHealthProvider
public class Building : NetworkBehaviour, IDamageable, IVisionProvider, ITeamMember, IHealthProvider, IInteractable
{
[Header("References")]
public TowerData buildingData;
@@ -66,6 +66,26 @@ namespace Northbound
private UnitHealthBar _healthBar;
private float _lastRegenTime;
// 업그레이드 관련
private NetworkVariable<bool> _isUpgrading = new NetworkVariable<bool>(
false,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Server
);
private NetworkVariable<float> _upgradeProgress = new NetworkVariable<float>(
0f,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Server
);
private NetworkVariable<int> _upgradeTargetId = new NetworkVariable<int>(
0,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Server
);
private float _lastInteractionTime;
private const float INTERACTION_COOLDOWN = 0.5f;
public override void OnNetworkSpawn()
{
base.OnNetworkSpawn();
@@ -108,6 +128,8 @@ namespace Northbound
// 이벤트 구독
_currentHealth.OnValueChanged += OnHealthValueChanged;
_team.OnValueChanged += OnTeamValueChanged;
_upgradeProgress.OnValueChanged += OnUpgradeProgressChanged;
_isUpgrading.OnValueChanged += OnUpgradingChanged;
// 체력바 생성
if (showHealthBar && healthBarPrefab != null)
@@ -124,6 +146,8 @@ namespace Northbound
{
_currentHealth.OnValueChanged -= OnHealthValueChanged;
_team.OnValueChanged -= OnTeamValueChanged;
_upgradeProgress.OnValueChanged -= OnUpgradeProgressChanged;
_isUpgrading.OnValueChanged -= OnUpgradingChanged;
// FogOfWar 시스템에서 제거
if (IsServer && buildingData != null && buildingData.providesVision)
@@ -480,6 +504,265 @@ namespace Northbound
#endregion
#region IInteractable Implementation (Upgrade)
/// <summary>
/// 상호작용 가능 여부
/// </summary>
public bool CanInteract(ulong playerId)
{
// 쿨다운 확인
if (Time.time - _lastInteractionTime < INTERACTION_COOLDOWN)
return false;
// 같은 팀만 업그레이드 가능
TeamType team = GetPlayerTeam(playerId);
if (team != _team.Value)
return false;
// 업그레이드 중이면 진행 가능
if (_isUpgrading.Value)
return true;
// 업그레이드 시작 가능 여부 확인
if (buildingData == null || buildingData.upgradeTo == 0)
return false;
TowerData upgradeData = GetUpgradeData();
if (upgradeData == null)
return false;
// 자원이 충분한지 확인
var coreResourceManager = CoreResourceManager.Instance;
if (coreResourceManager == null || !coreResourceManager.CanAfford(upgradeData.mana))
return false;
return true;
}
/// <summary>
/// 상호작용 실행 (업그레이드)
/// </summary>
public void Interact(ulong playerId)
{
if (!CanInteract(playerId))
return;
// 서버에 업그레이드 요청
RequestUpgradeServerRpc(playerId);
}
[Rpc(SendTo.Server)]
private void RequestUpgradeServerRpc(ulong playerId)
{
if (!CanInteract(playerId))
return;
_lastInteractionTime = Time.time;
// 업그레이드가 시작되지 않았으면 시작만 하고 종료
if (!_isUpgrading.Value)
{
TowerData upgradeData = GetUpgradeData();
if (upgradeData == null)
return;
// 자원 확인 및 소비
var coreResourceManager = CoreResourceManager.Instance;
if (coreResourceManager == null || !coreResourceManager.CanAfford(upgradeData.mana))
return;
coreResourceManager.SpendResources(upgradeData.mana);
// 업그레이드 시작 - 토대로 교체
StartUpgradeToFoundation(upgradeData);
return;
}
// 이미 업그레이드 중이면 플레이어의 manpower만큼 진행
float playerManpower = GetPlayerManpower(playerId);
_upgradeProgress.Value += playerManpower;
// 완료 체크
TowerData targetData = BuildingManager.Instance?.GetTowerDataById(_upgradeTargetId.Value);
if (targetData != null && _upgradeProgress.Value >= targetData.manpower)
{
CompleteUpgrade(targetData);
}
}
/// <summary>
/// 업그레이드 시작 - 건물을 토대로 교체
/// </summary>
private void StartUpgradeToFoundation(TowerData upgradeData)
{
if (!IsServer) return;
if (BuildingManager.Instance == null) return;
if (BuildingManager.Instance.foundationPrefab == null) return;
// 현재 건물 정보 저장
Vector3Int gridPos = gridPosition;
int rot = rotation;
ulong owner = _ownerId.Value;
TeamType team = _team.Value;
// BuildingManager에서 건물 제거
BuildingManager.Instance.RemoveBuilding(this);
// FogOfWar 시스템에서 제거
if (buildingData != null && buildingData.providesVision)
{
FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
}
// 현재 건물 제거
NetworkObject.Despawn(true);
// 토대 생성
Vector3 worldPosition = BuildingManager.Instance.GridToWorld(gridPos);
GameObject foundationObj = Instantiate(BuildingManager.Instance.foundationPrefab, worldPosition + upgradeData.placementOffset, Quaternion.Euler(0, rot * 90f, 0));
NetworkObject foundationNetObj = foundationObj.GetComponent<NetworkObject>();
if (foundationNetObj != null)
{
foundationNetObj.Spawn();
BuildingFoundation foundation = foundationObj.GetComponent<BuildingFoundation>();
if (foundation != null)
{
foundation.Initialize(upgradeData, gridPos, rot, owner, team);
BuildingManager.Instance.RegisterFoundation(foundation);
}
}
else
{
Destroy(foundationObj);
}
}
/// <summary>
/// 업그레이드 완료
/// </summary>
private void CompleteUpgrade(TowerData upgradeData)
{
if (!IsServer) return;
BuildingManager.Instance?.UpgradeBuilding(this, upgradeData);
}
/// <summary>
/// 상호작용 프롬프트 텍스트
/// </summary>
public string GetInteractionPrompt()
{
// 업그레이드 중이면 진행도 표시
if (_isUpgrading.Value)
{
TowerData targetData = BuildingManager.Instance?.GetTowerDataById(_upgradeTargetId.Value);
if (targetData != null)
{
float percentage = (_upgradeProgress.Value / targetData.manpower) * 100f;
return $"[E] Upgrading to {targetData.buildingName} ({percentage:F0}%)";
}
return "[E] Upgrading...";
}
TowerData upgradeData = GetUpgradeData();
if (upgradeData == null)
return "";
var coreResourceManager = CoreResourceManager.Instance;
bool canAfford = coreResourceManager != null && coreResourceManager.CanAfford(upgradeData.mana);
string costText = canAfford ? $"{upgradeData.mana}" : $"<color=red>{upgradeData.mana}</color>";
return $"[E] Upgrade to {upgradeData.buildingName} ({costText})";
}
public string GetInteractionAnimation()
{
return "Build"; // 건설 애니메이션 재사용
}
public EquipmentData GetEquipmentData()
{
return buildingData?.constructionEquipment;
}
Transform IInteractable.GetTransform()
{
return transform;
}
/// <summary>
/// 업그레이드 대상 TowerData 반환
/// </summary>
public TowerData GetUpgradeData()
{
if (buildingData == null || buildingData.upgradeTo == 0)
return null;
if (BuildingManager.Instance == null)
return null;
return BuildingManager.Instance.GetTowerDataById(buildingData.upgradeTo);
}
/// <summary>
/// 플레이어의 팀 가져오기
/// </summary>
private TeamType GetPlayerTeam(ulong playerId)
{
if (NetworkManager.Singleton != null && NetworkManager.Singleton.ConnectedClients != null)
{
if (NetworkManager.Singleton.ConnectedClients.TryGetValue(playerId, out var client))
{
if (client.PlayerObject != null)
{
var teamMember = client.PlayerObject.GetComponent<ITeamMember>();
if (teamMember != null)
{
return teamMember.GetTeam();
}
}
}
}
return TeamType.Player;
}
/// <summary>
/// 플레이어의 manpower 가져오기
/// </summary>
private float GetPlayerManpower(ulong playerId)
{
if (NetworkManager.Singleton != null && NetworkManager.Singleton.ConnectedClients != null)
{
if (NetworkManager.Singleton.ConnectedClients.TryGetValue(playerId, out var client))
{
if (client.PlayerObject != null)
{
var playerInteraction = client.PlayerObject.GetComponent<PlayerInteraction>();
if (playerInteraction != null)
{
return playerInteraction.WorkPower;
}
}
}
}
return 10f; // 기본값
}
private void OnUpgradeProgressChanged(float oldValue, float newValue)
{
// 업그레이드 진행 UI 업데이트 (필요시 구현)
}
private void OnUpgradingChanged(bool oldValue, bool newValue)
{
// 업그레이드 상태 변경 시 처리 (필요시 구현)
}
#endregion
#region Gizmos
private void OnDrawGizmos()

View File

@@ -296,6 +296,17 @@ namespace Northbound
}
}
/// <summary>
/// 토대 등록 (업그레이드 등으로 생성된 토대)
/// </summary>
public void RegisterFoundation(BuildingFoundation foundation)
{
if (!placedFoundations.Contains(foundation))
{
placedFoundations.Add(foundation);
}
}
/// <summary>
/// 사전 배치 건물 등록 (씬에 미리 있는 건물용)
/// </summary>
@@ -516,5 +527,91 @@ namespace Northbound
Destroy(buildingObj);
}
}
#region Upgrade System
/// <summary>
/// ID로 TowerData 찾기
/// </summary>
public TowerData GetTowerDataById(int towerId)
{
foreach (var data in availableBuildings)
{
if (data != null && data.id == towerId)
{
return data;
}
}
return null;
}
/// <summary>
/// 건물 업그레이드 (서버에서만 호출)
/// </summary>
public void UpgradeBuilding(Building building, TowerData upgradeData)
{
if (!IsServer) return;
if (building == null || upgradeData == null) return;
if (upgradeData.prefab == null)
{
Debug.LogError($"[BuildingManager] 업그레이드 프리팹이 없습니다: {upgradeData.buildingName}");
return;
}
// 기존 건물 정보 저장
Vector3Int gridPos = building.gridPosition;
int rotation = building.rotation;
ulong ownerId = building.GetOwnerId();
TeamType team = building.GetTeam();
// 기존 건물 제거
placedBuildings.Remove(building);
// FogOfWar 시스템에서 제거
if (building.buildingData != null && building.buildingData.providesVision)
{
FogOfWarSystem.Instance?.UnregisterVisionProvider(building);
}
// 기존 건물 파괴
building.NetworkObject.Despawn(true);
// 새 건물 생성
Vector3 worldPosition = GridToWorld(gridPos);
GameObject newBuildingObj = Instantiate(upgradeData.prefab, worldPosition + upgradeData.placementOffset, Quaternion.Euler(0, rotation * 90f, 0));
NetworkObject netObj = newBuildingObj.GetComponent<NetworkObject>();
// FogOfWarVisibility 추가
if (newBuildingObj.GetComponent<FogOfWarVisibility>() == null)
{
var visibility = newBuildingObj.AddComponent<FogOfWarVisibility>();
visibility.showInExploredAreas = true;
visibility.updateInterval = 0.2f;
}
if (netObj != null)
{
netObj.SpawnWithOwnership(ownerId);
Building newBuilding = newBuildingObj.GetComponent<Building>();
if (newBuilding == null)
{
newBuilding = newBuildingObj.AddComponent<Building>();
}
// 새 건물 초기화
newBuilding.Initialize(upgradeData, gridPos, rotation, ownerId, team);
placedBuildings.Add(newBuilding);
Debug.Log($"<color=green>[BuildingManager] 건물 업그레이드 완료: {upgradeData.buildingName}</color>");
}
else
{
Debug.LogError($"<color=red>[BuildingManager] 업그레이드 프리팹에 NetworkObject가 없습니다!</color>");
Destroy(newBuildingObj);
}
}
#endregion
}
}