건축 및 고용에 비용 소모 기능 추가

This commit is contained in:
2026-02-02 20:26:44 +09:00
parent 9dea9daaa9
commit 958ae1cb75
6 changed files with 159 additions and 11 deletions

View File

@@ -193,6 +193,22 @@ namespace Northbound
return; return;
} }
// 자원 확인 및 소비
var coreResourceManager = CoreResourceManager.Instance;
if (coreResourceManager == null)
{
Debug.LogWarning("<color=red>[BuildingManager] CoreResourceManager 인스턴스를 찾을 수 없습니다.</color>");
return;
}
if (!coreResourceManager.CanAfford(data.mana))
{
Debug.LogWarning($"<color=yellow>[BuildingManager] 코어 자원이 부족합니다. 필요: {data.mana} (클라이언트: {requestingClientId})</color>");
return;
}
coreResourceManager.SpendResources(data.mana);
// 배치 가능 여부 확인 // 배치 가능 여부 확인
if (!IsValidPlacement(data, position, rotation, out Vector3 snappedPosition)) if (!IsValidPlacement(data, position, rotation, out Vector3 snappedPosition))
{ {
@@ -345,6 +361,22 @@ namespace Northbound
return; return;
} }
// 자원 확인 및 소비
var coreResourceManager = CoreResourceManager.Instance;
if (coreResourceManager == null)
{
Debug.LogWarning("<color=red>[BuildingManager] CoreResourceManager 인스턴스를 찾을 수 없습니다.</color>");
return;
}
if (!coreResourceManager.CanAfford(data.mana))
{
Debug.LogWarning($"<color=yellow>[BuildingManager] 코어 자원이 부족합니다. 필요: {data.mana}</color>");
return;
}
coreResourceManager.SpendResources(data.mana);
// 토대 프리팹 확인 // 토대 프리팹 확인
if (foundationPrefab == null) if (foundationPrefab == null)
{ {

View File

@@ -332,12 +332,16 @@ namespace Northbound
// Check if placement is valid // Check if placement is valid
bool isValid = BuildingManager.Instance.IsValidPlacement(data, hit.point, currentRotation, out Vector3 snappedPosition); 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 적용) // Update preview position (placementOffset 적용)
previewObject.transform.position = snappedPosition + data.placementOffset; previewObject.transform.position = snappedPosition + data.placementOffset;
previewObject.transform.rotation = Quaternion.Euler(0, currentRotation * 90f, 0); previewObject.transform.rotation = Quaternion.Euler(0, currentRotation * 90f, 0);
// Update material based on validity // Update material based on validity and affordability
Material targetMat = isValid ? validMaterial : invalidMaterial; Material targetMat = (isValid && canAfford) ? validMaterial : invalidMaterial;
foreach (var renderer in previewRenderers) foreach (var renderer in previewRenderers)
{ {
Material[] mats = new Material[renderer.materials.Length]; Material[] mats = new Material[renderer.materials.Length];
@@ -462,6 +466,10 @@ namespace Northbound
Vector3 dragEndPosition = hit.point; Vector3 dragEndPosition = hit.point;
List<Vector3> positions = CalculateDragBuildingPositions(dragStartPosition, dragEndPosition, data); List<Vector3> 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(); ClearDragPreviews();
@@ -490,7 +498,7 @@ namespace Northbound
preview.transform.rotation = Quaternion.Euler(0, currentRotation * 90f, 0); preview.transform.rotation = Quaternion.Euler(0, currentRotation * 90f, 0);
// Apply materials // Apply materials
Material targetMat = isValid ? validMaterial : invalidMaterial; Material targetMat = (isValid && canAffordAll) ? validMaterial : invalidMaterial;
Renderer[] renderers = preview.GetComponentsInChildren<Renderer>(); Renderer[] renderers = preview.GetComponentsInChildren<Renderer>();
foreach (var renderer in renderers) foreach (var renderer in renderers)
{ {
@@ -569,6 +577,14 @@ namespace Northbound
TowerData selectedData = BuildingManager.Instance.availableBuildings[selectedBuildingIndex]; TowerData selectedData = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
// Check affordability
var coreResourceManager = CoreResourceManager.Instance;
if (coreResourceManager == null || !coreResourceManager.CanAfford(selectedData.mana * dragBuildingPositions.Count))
{
Debug.Log("<color=yellow>[BuildingPlacement] 자원이 부족합니다.</color>");
return;
}
int successCount = 0; int successCount = 0;
foreach (var position in dragBuildingPositions) foreach (var position in dragBuildingPositions)
{ {
@@ -609,6 +625,14 @@ namespace Northbound
{ {
TowerData selectedData = BuildingManager.Instance.availableBuildings[selectedBuildingIndex]; TowerData selectedData = BuildingManager.Instance.availableBuildings[selectedBuildingIndex];
// Check affordability
var coreResourceManager = CoreResourceManager.Instance;
if (coreResourceManager != null && !coreResourceManager.CanAfford(selectedData.mana))
{
Debug.Log("<color=yellow>[BuildingPlacement] 자원이 부족합니다.</color>");
return;
}
if (BuildingManager.Instance.IsValidPlacement(selectedData, hit.point, currentRotation, out Vector3 groundPosition)) if (BuildingManager.Instance.IsValidPlacement(selectedData, hit.point, currentRotation, out Vector3 groundPosition))
{ {
// 토대 배치 요청 // 토대 배치 요청

View File

@@ -68,6 +68,28 @@ namespace Northbound
InitializeSlots(); InitializeSlots();
} }
private void Update()
{
if (quickslotPanel != null && quickslotPanel.activeSelf)
{
UpdateCostDisplays();
}
}
/// <summary>
/// 모든 슬롯의 비용 표시 업데이트
/// </summary>
public void UpdateCostDisplays()
{
foreach (var slot in slotButtons)
{
if (slot != null)
{
slot.UpdateCostDisplay();
}
}
}
/// <summary> /// <summary>
/// 퀵슬롯 Input Actions 초기화 및 구독 (직접 참조) /// 퀵슬롯 Input Actions 초기화 및 구독 (직접 참조)
/// </summary> /// </summary>

View File

@@ -52,6 +52,20 @@ namespace Northbound
UpdateVisuals(); UpdateVisuals();
} }
/// <summary>
/// 비용 표시 업데이트 (자원 변화 시 호출)
/// </summary>
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}" : $"<color=red>{buildingData.mana}</color>";
nameText.text = $"{buildingData.buildingName}\n비용: {costText}";
}
}
/// <summary> /// <summary>
/// UI 업데이트 /// UI 업데이트
/// </summary> /// </summary>
@@ -75,7 +89,10 @@ namespace Northbound
// 이름 설정 // 이름 설정
if (nameText != null) if (nameText != null)
{ {
nameText.text = buildingData.buildingName; var coreResourceManager = CoreResourceManager.Instance;
bool canAfford = coreResourceManager != null && coreResourceManager.CanAfford(buildingData.mana);
string costText = canAfford ? $"{buildingData.mana}" : $"<color=red>{buildingData.mana}</color>";
nameText.text = $"{buildingData.buildingName}\n비용: {costText}";
} }
// 배경 색상 // 배경 색상

View File

@@ -20,6 +20,7 @@ namespace Northbound
public float followDistance = 3f; public float followDistance = 3f;
public float movementSpeed = 3.5f; public float movementSpeed = 3.5f;
public int resourcesPerMining = 5; public int resourcesPerMining = 5;
public int recruitmentCost = 10;
[Header("Interaction")] [Header("Interaction")]
public string interactionAnimationTrigger = "Recruit"; public string interactionAnimationTrigger = "Recruit";
@@ -320,7 +321,17 @@ namespace Northbound
public bool CanInteract(ulong playerId) 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) public void Interact(ulong playerId)
@@ -348,6 +359,20 @@ namespace Northbound
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)] [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
private void RecruitWorkerServerRpc(ulong playerId, ulong workerNetObjectId) 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; _ownerPlayerId.Value = playerId;
SetState(WorkerState.Following); SetState(WorkerState.Following);
UpdatePlayerTransform(); UpdatePlayerTransform();
@@ -389,7 +414,11 @@ namespace Northbound
{ {
if (_ownerPlayerId.Value == ulong.MaxValue) 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) else if (NetworkManager.Singleton != null && _ownerPlayerId.Value == NetworkManager.Singleton.LocalClientId)
{ {

View File

@@ -11,6 +11,7 @@ namespace Northbound
public float spawnRadius = 2f; public float spawnRadius = 2f;
public int maxWorkers = 5; public int maxWorkers = 5;
public float spawnCooldown = 5f; public float spawnCooldown = 5f;
public int recruitmentCost = 20;
[Header("Interaction")] [Header("Interaction")]
public string interactionAnimationTrigger = "Build"; public string interactionAnimationTrigger = "Build";
@@ -59,6 +60,10 @@ namespace Northbound
if (workerPrefab == null) if (workerPrefab == null)
return false; return false;
var coreResourceManager = CoreResourceManager.Instance;
if (coreResourceManager != null && !coreResourceManager.CanAfford(recruitmentCost))
return false;
return true; return true;
} }
@@ -82,6 +87,21 @@ namespace Northbound
return; 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; Vector3 spawnPosition = spawnPoint != null ? spawnPoint.position : transform.position;
float randomAngle = Random.Range(0f, 360f); float randomAngle = Random.Range(0f, 360f);
@@ -185,7 +205,11 @@ namespace Northbound
if (cooldownRemaining > 0) if (cooldownRemaining > 0)
return $"워커 생성 대기 중 ({cooldownRemaining:F1}s)"; 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() public string GetInteractionAnimation()