diff --git a/Assets/Scripts/Building.cs b/Assets/Scripts/Building.cs index eab42ac..ab6f893 100644 --- a/Assets/Scripts/Building.cs +++ b/Assets/Scripts/Building.cs @@ -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 _isUpgrading = new NetworkVariable( + false, + NetworkVariableReadPermission.Everyone, + NetworkVariableWritePermission.Server + ); + private NetworkVariable _upgradeProgress = new NetworkVariable( + 0f, + NetworkVariableReadPermission.Everyone, + NetworkVariableWritePermission.Server + ); + private NetworkVariable _upgradeTargetId = new NetworkVariable( + 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) + + /// + /// 상호작용 가능 여부 + /// + 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; + } + + /// + /// 상호작용 실행 (업그레이드) + /// + 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); + } + } + + /// + /// 업그레이드 시작 - 건물을 토대로 교체 + /// + 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(); + + if (foundationNetObj != null) + { + foundationNetObj.Spawn(); + + BuildingFoundation foundation = foundationObj.GetComponent(); + if (foundation != null) + { + foundation.Initialize(upgradeData, gridPos, rot, owner, team); + BuildingManager.Instance.RegisterFoundation(foundation); + } + } + else + { + Destroy(foundationObj); + } + } + + /// + /// 업그레이드 완료 + /// + private void CompleteUpgrade(TowerData upgradeData) + { + if (!IsServer) return; + + BuildingManager.Instance?.UpgradeBuilding(this, upgradeData); + } + + /// + /// 상호작용 프롬프트 텍스트 + /// + 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}" : $"{upgradeData.mana}"; + return $"[E] Upgrade to {upgradeData.buildingName} ({costText})"; + } + + public string GetInteractionAnimation() + { + return "Build"; // 건설 애니메이션 재사용 + } + + public EquipmentData GetEquipmentData() + { + return buildingData?.constructionEquipment; + } + + Transform IInteractable.GetTransform() + { + return transform; + } + + /// + /// 업그레이드 대상 TowerData 반환 + /// + public TowerData GetUpgradeData() + { + if (buildingData == null || buildingData.upgradeTo == 0) + return null; + + if (BuildingManager.Instance == null) + return null; + + return BuildingManager.Instance.GetTowerDataById(buildingData.upgradeTo); + } + + /// + /// 플레이어의 팀 가져오기 + /// + 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(); + if (teamMember != null) + { + return teamMember.GetTeam(); + } + } + } + } + return TeamType.Player; + } + + /// + /// 플레이어의 manpower 가져오기 + /// + 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(); + 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() diff --git a/Assets/Scripts/BuildingManager.cs b/Assets/Scripts/BuildingManager.cs index 20f8778..02c58e4 100644 --- a/Assets/Scripts/BuildingManager.cs +++ b/Assets/Scripts/BuildingManager.cs @@ -296,6 +296,17 @@ namespace Northbound } } + /// + /// 토대 등록 (업그레이드 등으로 생성된 토대) + /// + public void RegisterFoundation(BuildingFoundation foundation) + { + if (!placedFoundations.Contains(foundation)) + { + placedFoundations.Add(foundation); + } + } + /// /// 사전 배치 건물 등록 (씬에 미리 있는 건물용) /// @@ -516,5 +527,91 @@ namespace Northbound Destroy(buildingObj); } } + + #region Upgrade System + + /// + /// ID로 TowerData 찾기 + /// + public TowerData GetTowerDataById(int towerId) + { + foreach (var data in availableBuildings) + { + if (data != null && data.id == towerId) + { + return data; + } + } + return null; + } + + /// + /// 건물 업그레이드 (서버에서만 호출) + /// + 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(); + + // FogOfWarVisibility 추가 + if (newBuildingObj.GetComponent() == null) + { + var visibility = newBuildingObj.AddComponent(); + visibility.showInExploredAreas = true; + visibility.updateInterval = 0.2f; + } + + if (netObj != null) + { + netObj.SpawnWithOwnership(ownerId); + + Building newBuilding = newBuildingObj.GetComponent(); + if (newBuilding == null) + { + newBuilding = newBuildingObj.AddComponent(); + } + + // 새 건물 초기화 + newBuilding.Initialize(upgradeData, gridPos, rotation, ownerId, team); + placedBuildings.Add(newBuilding); + + Debug.Log($"[BuildingManager] 건물 업그레이드 완료: {upgradeData.buildingName}"); + } + else + { + Debug.LogError($"[BuildingManager] 업그레이드 프리팹에 NetworkObject가 없습니다!"); + Destroy(newBuildingObj); + } + } + + #endregion } }