From 958ae1cb75851fba4b2fd82aede0e78531ea9f82 Mon Sep 17 00:00:00 2001 From: dal4segno Date: Mon, 2 Feb 2026 20:26:44 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B1=B4=EC=B6=95=20=EB=B0=8F=20=EA=B3=A0?= =?UTF-8?q?=EC=9A=A9=EC=97=90=20=EB=B9=84=EC=9A=A9=20=EC=86=8C=EB=AA=A8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Scripts/BuildingManager.cs | 32 ++++++++++++++++++++++ Assets/Scripts/BuildingPlacement.cs | 38 ++++++++++++++++++++++----- Assets/Scripts/BuildingQuickslotUI.cs | 22 ++++++++++++++++ Assets/Scripts/BuildingSlotButton.cs | 19 +++++++++++++- Assets/Scripts/Worker.cs | 33 +++++++++++++++++++++-- Assets/Scripts/WorkerSpawner.cs | 26 +++++++++++++++++- 6 files changed, 159 insertions(+), 11 deletions(-) diff --git a/Assets/Scripts/BuildingManager.cs b/Assets/Scripts/BuildingManager.cs index a774be4..68dbe94 100644 --- a/Assets/Scripts/BuildingManager.cs +++ b/Assets/Scripts/BuildingManager.cs @@ -193,6 +193,22 @@ namespace Northbound return; } + // 자원 확인 및 소비 + var coreResourceManager = CoreResourceManager.Instance; + if (coreResourceManager == null) + { + Debug.LogWarning("[BuildingManager] CoreResourceManager 인스턴스를 찾을 수 없습니다."); + return; + } + + if (!coreResourceManager.CanAfford(data.mana)) + { + Debug.LogWarning($"[BuildingManager] 코어 자원이 부족합니다. 필요: {data.mana} (클라이언트: {requestingClientId})"); + return; + } + + coreResourceManager.SpendResources(data.mana); + // 배치 가능 여부 확인 if (!IsValidPlacement(data, position, rotation, out Vector3 snappedPosition)) { @@ -345,6 +361,22 @@ namespace Northbound return; } + // 자원 확인 및 소비 + var coreResourceManager = CoreResourceManager.Instance; + if (coreResourceManager == null) + { + Debug.LogWarning("[BuildingManager] CoreResourceManager 인스턴스를 찾을 수 없습니다."); + return; + } + + if (!coreResourceManager.CanAfford(data.mana)) + { + Debug.LogWarning($"[BuildingManager] 코어 자원이 부족합니다. 필요: {data.mana}"); + return; + } + + coreResourceManager.SpendResources(data.mana); + // 토대 프리팹 확인 if (foundationPrefab == null) { diff --git a/Assets/Scripts/BuildingPlacement.cs b/Assets/Scripts/BuildingPlacement.cs index 6d1b78f..52014da 100644 --- a/Assets/Scripts/BuildingPlacement.cs +++ b/Assets/Scripts/BuildingPlacement.cs @@ -328,16 +328,20 @@ namespace Northbound if (Physics.Raycast(ray, out RaycastHit hit, maxPlacementDistance, groundLayer)) { - + // Check if placement is valid bool isValid = BuildingManager.Instance.IsValidPlacement(data, hit.point, currentRotation, out Vector3 snappedPosition); + // Check affordability + var coreResourceManager = CoreResourceManager.Instance; + bool canAfford = coreResourceManager != null && coreResourceManager.CanAfford(data.mana); + // Update preview position (placementOffset 적용) previewObject.transform.position = snappedPosition + data.placementOffset; previewObject.transform.rotation = Quaternion.Euler(0, currentRotation * 90f, 0); - // Update material based on validity - Material targetMat = isValid ? validMaterial : invalidMaterial; + // Update material based on validity and affordability + Material targetMat = (isValid && canAfford) ? validMaterial : invalidMaterial; foreach (var renderer in previewRenderers) { Material[] mats = new Material[renderer.materials.Length]; @@ -462,6 +466,10 @@ namespace Northbound Vector3 dragEndPosition = hit.point; List positions = CalculateDragBuildingPositions(dragStartPosition, dragEndPosition, data); + // Check affordability for all buildings + var coreResourceManager = CoreResourceManager.Instance; + bool canAffordAll = coreResourceManager != null && coreResourceManager.CanAfford(data.mana * positions.Count); + // 기존 프리뷰 정리 ClearDragPreviews(); @@ -476,9 +484,9 @@ namespace Northbound } bool isValid = BuildingManager.Instance.IsValidPlacement(data, pos, currentRotation, out Vector3 snappedPosition); - + GameObject preview = Instantiate(data.prefab); - + // Remove NetworkObject and Building components if (preview.GetComponent() != null) Destroy(preview.GetComponent()); @@ -490,7 +498,7 @@ namespace Northbound preview.transform.rotation = Quaternion.Euler(0, currentRotation * 90f, 0); // Apply materials - Material targetMat = isValid ? validMaterial : invalidMaterial; + Material targetMat = (isValid && canAffordAll) ? validMaterial : invalidMaterial; Renderer[] renderers = preview.GetComponentsInChildren(); foreach (var renderer in renderers) { @@ -569,6 +577,14 @@ namespace Northbound TowerData selectedData = BuildingManager.Instance.availableBuildings[selectedBuildingIndex]; + // Check affordability + var coreResourceManager = CoreResourceManager.Instance; + if (coreResourceManager == null || !coreResourceManager.CanAfford(selectedData.mana * dragBuildingPositions.Count)) + { + Debug.Log("[BuildingPlacement] 자원이 부족합니다."); + return; + } + int successCount = 0; foreach (var position in dragBuildingPositions) { @@ -608,7 +624,15 @@ namespace Northbound if (Physics.Raycast(ray, out RaycastHit hit, maxPlacementDistance, groundLayer)) { TowerData selectedData = BuildingManager.Instance.availableBuildings[selectedBuildingIndex]; - + + // Check affordability + var coreResourceManager = CoreResourceManager.Instance; + if (coreResourceManager != null && !coreResourceManager.CanAfford(selectedData.mana)) + { + Debug.Log("[BuildingPlacement] 자원이 부족합니다."); + return; + } + if (BuildingManager.Instance.IsValidPlacement(selectedData, hit.point, currentRotation, out Vector3 groundPosition)) { // 토대 배치 요청 diff --git a/Assets/Scripts/BuildingQuickslotUI.cs b/Assets/Scripts/BuildingQuickslotUI.cs index eb2eaa6..d360727 100644 --- a/Assets/Scripts/BuildingQuickslotUI.cs +++ b/Assets/Scripts/BuildingQuickslotUI.cs @@ -68,6 +68,28 @@ namespace Northbound InitializeSlots(); } + private void Update() + { + if (quickslotPanel != null && quickslotPanel.activeSelf) + { + UpdateCostDisplays(); + } + } + + /// + /// 모든 슬롯의 비용 표시 업데이트 + /// + public void UpdateCostDisplays() + { + foreach (var slot in slotButtons) + { + if (slot != null) + { + slot.UpdateCostDisplay(); + } + } + } + /// /// 퀵슬롯 Input Actions 초기화 및 구독 (직접 참조) /// diff --git a/Assets/Scripts/BuildingSlotButton.cs b/Assets/Scripts/BuildingSlotButton.cs index 2a94241..b2150de 100644 --- a/Assets/Scripts/BuildingSlotButton.cs +++ b/Assets/Scripts/BuildingSlotButton.cs @@ -52,6 +52,20 @@ namespace Northbound UpdateVisuals(); } + /// + /// 비용 표시 업데이트 (자원 변화 시 호출) + /// + public void UpdateCostDisplay() + { + if (nameText != null && buildingData != null) + { + var coreResourceManager = CoreResourceManager.Instance; + bool canAfford = coreResourceManager != null && coreResourceManager.CanAfford(buildingData.mana); + string costText = canAfford ? $"{buildingData.mana}" : $"{buildingData.mana}"; + nameText.text = $"{buildingData.buildingName}\n비용: {costText}"; + } + } + /// /// UI 업데이트 /// @@ -75,7 +89,10 @@ namespace Northbound // 이름 설정 if (nameText != null) { - nameText.text = buildingData.buildingName; + var coreResourceManager = CoreResourceManager.Instance; + bool canAfford = coreResourceManager != null && coreResourceManager.CanAfford(buildingData.mana); + string costText = canAfford ? $"{buildingData.mana}" : $"{buildingData.mana}"; + nameText.text = $"{buildingData.buildingName}\n비용: {costText}"; } // 배경 색상 diff --git a/Assets/Scripts/Worker.cs b/Assets/Scripts/Worker.cs index f203b3d..b2c3a0c 100644 --- a/Assets/Scripts/Worker.cs +++ b/Assets/Scripts/Worker.cs @@ -20,6 +20,7 @@ namespace Northbound public float followDistance = 3f; public float movementSpeed = 3.5f; public int resourcesPerMining = 5; + public int recruitmentCost = 10; [Header("Interaction")] public string interactionAnimationTrigger = "Recruit"; @@ -320,7 +321,17 @@ namespace Northbound public bool CanInteract(ulong playerId) { - return _ownerPlayerId.Value == ulong.MaxValue || _ownerPlayerId.Value == playerId; + if (_ownerPlayerId.Value != ulong.MaxValue && _ownerPlayerId.Value != playerId) + return false; + + if (_ownerPlayerId.Value == ulong.MaxValue) + { + var coreResourceManager = CoreResourceManager.Instance; + if (coreResourceManager != null && !coreResourceManager.CanAfford(recruitmentCost)) + return false; + } + + return true; } public void Interact(ulong playerId) @@ -348,6 +359,20 @@ namespace Northbound [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)] private void RecruitWorkerServerRpc(ulong playerId, ulong workerNetObjectId) { + var coreResourceManager = CoreResourceManager.Instance; + if (coreResourceManager == null) + { + Debug.LogWarning("[Worker] CoreResourceManager 인스턴스를 찾을 수 없습니다."); + return; + } + + if (!coreResourceManager.CanAfford(recruitmentCost)) + { + Debug.LogWarning($"[Worker] 코어 자원이 부족합니다. 필요: {recruitmentCost}"); + return; + } + + coreResourceManager.SpendResources(recruitmentCost); _ownerPlayerId.Value = playerId; SetState(WorkerState.Following); UpdatePlayerTransform(); @@ -389,7 +414,11 @@ namespace Northbound { if (_ownerPlayerId.Value == ulong.MaxValue) { - return "[E] 워커 채용"; + var coreResourceManager = CoreResourceManager.Instance; + if (coreResourceManager != null && !coreResourceManager.CanAfford(recruitmentCost)) + return $"자원 부족 (필요: {recruitmentCost})"; + + return $"[E] 워커 채용 - 비용: {recruitmentCost}"; } else if (NetworkManager.Singleton != null && _ownerPlayerId.Value == NetworkManager.Singleton.LocalClientId) { diff --git a/Assets/Scripts/WorkerSpawner.cs b/Assets/Scripts/WorkerSpawner.cs index 8e31aed..075700b 100644 --- a/Assets/Scripts/WorkerSpawner.cs +++ b/Assets/Scripts/WorkerSpawner.cs @@ -11,6 +11,7 @@ namespace Northbound public float spawnRadius = 2f; public int maxWorkers = 5; public float spawnCooldown = 5f; + public int recruitmentCost = 20; [Header("Interaction")] public string interactionAnimationTrigger = "Build"; @@ -59,6 +60,10 @@ namespace Northbound if (workerPrefab == null) return false; + var coreResourceManager = CoreResourceManager.Instance; + if (coreResourceManager != null && !coreResourceManager.CanAfford(recruitmentCost)) + return false; + return true; } @@ -82,6 +87,21 @@ namespace Northbound return; } + var coreResourceManager = CoreResourceManager.Instance; + if (coreResourceManager == null) + { + Debug.LogWarning("[WorkerSpawner] CoreResourceManager 인스턴스를 찾을 수 없습니다."); + return; + } + + if (!coreResourceManager.CanAfford(recruitmentCost)) + { + Debug.LogWarning($"[WorkerSpawner] 코어 자원이 부족합니다. 필요: {recruitmentCost}"); + return; + } + + coreResourceManager.SpendResources(recruitmentCost); + Vector3 spawnPosition = spawnPoint != null ? spawnPoint.position : transform.position; float randomAngle = Random.Range(0f, 360f); @@ -185,7 +205,11 @@ namespace Northbound if (cooldownRemaining > 0) return $"워커 생성 대기 중 ({cooldownRemaining:F1}s)"; - return $"{interactionPrompt} ({_workerCount.Value}/{maxWorkers})"; + var coreResourceManager = CoreResourceManager.Instance; + if (coreResourceManager != null && !coreResourceManager.CanAfford(recruitmentCost)) + return $"자원 부족 (필요: {recruitmentCost})"; + + return $"{interactionPrompt} ({_workerCount.Value}/{maxWorkers}) - 비용: {recruitmentCost}"; } public string GetInteractionAnimation()