대장간을 이용한 업그레이드 기능 추가
This commit is contained in:
8
Assets/Scripts/Buildings.meta
Normal file
8
Assets/Scripts/Buildings.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3cdca32e565bc4942912b9bc2233349e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
126
Assets/Scripts/Buildings/Blacksmith.cs
Normal file
126
Assets/Scripts/Buildings/Blacksmith.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using UnityEngine;
|
||||
using Unity.Netcode;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 블랙스미스 건물 - 업그레이드 구매 가능
|
||||
/// </summary>
|
||||
public class Blacksmith : MonoBehaviour, IInteractable
|
||||
{
|
||||
[Header("UI Reference")]
|
||||
[SerializeField] private GameObject _upgradePopupPrefab;
|
||||
|
||||
private GameObject _popupInstance;
|
||||
private UpgradeListPopup _upgradePopup;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// 프리팹이 없으면 Resources에서 로드
|
||||
if (_upgradePopupPrefab == null)
|
||||
{
|
||||
_upgradePopupPrefab = Resources.Load<GameObject>("UI/Upgrade/UpgradeListPopup");
|
||||
}
|
||||
}
|
||||
|
||||
#region IInteractable Implementation
|
||||
|
||||
public bool CanInteract(ulong playerId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Interact(ulong playerId)
|
||||
{
|
||||
OpenUpgradePopup(playerId);
|
||||
}
|
||||
|
||||
public string GetInteractionPrompt()
|
||||
{
|
||||
return "[F] 업그레이드";
|
||||
}
|
||||
|
||||
public string GetInteractionAnimation()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public EquipmentData GetEquipmentData()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public Transform GetTransform()
|
||||
{
|
||||
return transform;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
private void OpenUpgradePopup(ulong playerId)
|
||||
{
|
||||
// 플레이어의 UpgradeManager와 Stats 찾기
|
||||
var playerObj = GetPlayerObject(playerId);
|
||||
if (playerObj == null)
|
||||
{
|
||||
Debug.LogWarning($"[Blacksmith] 플레이어 {playerId}를 찾을 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
var upgradeManager = playerObj.GetComponent<PlayerUpgradeManager>();
|
||||
var playerStats = playerObj.GetComponent<PlayerStats>();
|
||||
|
||||
if (upgradeManager == null || playerStats == null)
|
||||
{
|
||||
Debug.LogWarning("[Blacksmith] PlayerUpgradeManager 또는 PlayerStats가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 팝업 생성
|
||||
if (_popupInstance == null && _upgradePopupPrefab != null)
|
||||
{
|
||||
_popupInstance = Instantiate(_upgradePopupPrefab);
|
||||
_upgradePopup = _popupInstance.GetComponent<UpgradeListPopup>();
|
||||
|
||||
if (_upgradePopup == null)
|
||||
{
|
||||
_upgradePopup = _popupInstance.AddComponent<UpgradeListPopup>();
|
||||
}
|
||||
}
|
||||
|
||||
// 팝업 초기화 및 열기
|
||||
if (_upgradePopup != null)
|
||||
{
|
||||
_upgradePopup.Initialize(upgradeManager, playerStats);
|
||||
}
|
||||
}
|
||||
|
||||
private GameObject GetPlayerObject(ulong playerId)
|
||||
{
|
||||
if (NetworkManager.Singleton == null) return null;
|
||||
|
||||
// NetworkManager에서 플레이어 오브젝트 찾기
|
||||
foreach (var netObj in NetworkManager.Singleton.SpawnManager.SpawnedObjects.Values)
|
||||
{
|
||||
var controller = netObj.GetComponent<NetworkPlayerController>();
|
||||
if (controller != null && controller.OwnerPlayerId == playerId)
|
||||
{
|
||||
return netObj.gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_popupInstance != null)
|
||||
{
|
||||
Destroy(_popupInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Buildings/Blacksmith.cs.meta
Normal file
2
Assets/Scripts/Buildings/Blacksmith.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39d108eb3babe48409cc4dfc803dbbe4
|
||||
@@ -140,5 +140,26 @@ namespace Northbound
|
||||
public void SetBaseAttackRange(float value) => baseAttackRange = value;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 현재 스탯 정보 출력 (디버그용)
|
||||
/// </summary>
|
||||
public void PrintCurrentStats()
|
||||
{
|
||||
Debug.Log($"<color=cyan>[PlayerStats] 현재 스탯:</color>\n" +
|
||||
$" Max HP: {GetMaxHp()} (기본: {baseMaxHp})\n" +
|
||||
$" Damage: {GetDamage()} (기본: {baseDamage})\n" +
|
||||
$" Capacity: {GetCapacity()} (기본: {baseCapacity})\n" +
|
||||
$" Manpower: {GetManpower()} (기본: {baseManpower})\n" +
|
||||
$" Move Speed: {GetMoveSpeed()} (기본: {baseMoveSpeed})\n" +
|
||||
$" Sight: {GetSight()} (기본: {baseSight})\n" +
|
||||
$" Attack Range: {GetAttackRange()} (기본: {baseAttackRange})");
|
||||
|
||||
if (_upgradeManager != null)
|
||||
{
|
||||
var ownedIds = _upgradeManager.GetOwnedUpgradeIdList();
|
||||
Debug.Log($"<color=cyan>[PlayerStats] 보유 업그레이드 수: {ownedIds.Count}</color>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,10 @@ namespace Northbound
|
||||
|
||||
Debug.Log($"<color=green>[PlayerUpgradeManager] 업그레이드 '{upgrade.memo}' 구매 완료! (ID: {upgradeId})</color>");
|
||||
|
||||
// 스탯 변경 확인
|
||||
var playerStats = GetComponent<PlayerStats>();
|
||||
playerStats?.PrintCurrentStats();
|
||||
|
||||
// 이벤트 발생
|
||||
OnUpgradePurchased?.Invoke(upgradeId);
|
||||
}
|
||||
|
||||
8
Assets/Scripts/UI.meta
Normal file
8
Assets/Scripts/UI.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00eb5c70ed6dd1840841026ff5a16805
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
228
Assets/Scripts/UI/UpgradeListItem.cs
Normal file
228
Assets/Scripts/UI/UpgradeListItem.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
using Northbound.Data;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 업그레이드 리스트 아이템 UI
|
||||
/// </summary>
|
||||
public class UpgradeListItem : MonoBehaviour, IPointerClickHandler
|
||||
{
|
||||
[Header("UI References")]
|
||||
[SerializeField] private TextMeshProUGUI _nameText;
|
||||
[SerializeField] private TextMeshProUGUI _costText;
|
||||
[SerializeField] private Transform _effectRowsParent;
|
||||
[SerializeField] private Transform _reqChipsParent;
|
||||
[SerializeField] private Button _purchaseButton;
|
||||
[SerializeField] private GameObject _purchasedOverlay;
|
||||
[SerializeField] private Image _backgroundImage;
|
||||
|
||||
private UpgradeData _upgradeData;
|
||||
private bool _isOwned;
|
||||
private bool _canPurchase;
|
||||
private UpgradeListPopup _parentPopup;
|
||||
|
||||
private Color _normalColor = new Color(1f, 1f, 1f, 0.44f); // 기본 배경색
|
||||
private Color _disabledColor = new Color(0.5f, 0.5f, 0.5f, 0.6f); // 회색
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_purchaseButton == null)
|
||||
{
|
||||
_purchaseButton = GetComponent<Button>();
|
||||
}
|
||||
|
||||
if (_purchaseButton != null)
|
||||
{
|
||||
_purchaseButton.onClick.AddListener(OnPurchaseClicked);
|
||||
}
|
||||
|
||||
if (_backgroundImage == null)
|
||||
{
|
||||
_backgroundImage = GetComponent<Image>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이템 설정
|
||||
/// </summary>
|
||||
public void Setup(UpgradeData upgrade, bool isOwned, bool canPurchase, UpgradeListPopup parentPopup)
|
||||
{
|
||||
_upgradeData = upgrade;
|
||||
_isOwned = isOwned;
|
||||
_canPurchase = canPurchase;
|
||||
_parentPopup = parentPopup;
|
||||
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
private void UpdateUI()
|
||||
{
|
||||
if (_upgradeData == null) return;
|
||||
|
||||
// 이름
|
||||
if (_nameText != null)
|
||||
{
|
||||
_nameText.text = _upgradeData.memo;
|
||||
}
|
||||
|
||||
// 비용
|
||||
if (_costText != null)
|
||||
{
|
||||
_costText.text = $"Cost: {_upgradeData.mana}";
|
||||
}
|
||||
|
||||
// 구매 가능 여부 확인 (비용 체크 포함)
|
||||
bool hasEnoughResources = CheckHasEnoughResources();
|
||||
bool isAvailable = _canPurchase && !_isOwned && hasEnoughResources;
|
||||
|
||||
// 구매 불가능한 경우 회색 처리
|
||||
if (_backgroundImage != null)
|
||||
{
|
||||
_backgroundImage.color = isAvailable ? _normalColor : _disabledColor;
|
||||
}
|
||||
|
||||
// 이름 텍스트 색상도 변경
|
||||
if (_nameText != null)
|
||||
{
|
||||
_nameText.color = isAvailable ? Color.white : Color.gray;
|
||||
}
|
||||
|
||||
// 비용 텍스트 색상 (자원 부족 시 빨간색)
|
||||
if (_costText != null)
|
||||
{
|
||||
_costText.color = hasEnoughResources ? Color.white : Color.red;
|
||||
}
|
||||
|
||||
// 구매 버튼 - 항상 클릭 가능하게 설정
|
||||
if (_purchaseButton != null)
|
||||
{
|
||||
_purchaseButton.interactable = true;
|
||||
}
|
||||
|
||||
// 구매 완료 오버레이
|
||||
if (_purchasedOverlay != null)
|
||||
{
|
||||
_purchasedOverlay.SetActive(_isOwned);
|
||||
}
|
||||
|
||||
// 효과 표시
|
||||
UpdateEffectRows();
|
||||
|
||||
// 선행조건 표시
|
||||
UpdateReqChips();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 자원이 충분한지 확인
|
||||
/// </summary>
|
||||
private bool CheckHasEnoughResources()
|
||||
{
|
||||
var core = CoreResourceManager.Instance?.mainCore;
|
||||
if (core == null) return false;
|
||||
return core.TotalResources >= _upgradeData.mana;
|
||||
}
|
||||
|
||||
private void UpdateEffectRows()
|
||||
{
|
||||
if (_effectRowsParent == null || _upgradeData == null) return;
|
||||
|
||||
// 기존 자식 제거
|
||||
foreach (Transform child in _effectRowsParent)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
// 효과 목록 생성
|
||||
for (int i = 0; i < _upgradeData.effectStatList.Count; i++)
|
||||
{
|
||||
if (i >= _upgradeData.effectOpList.Count || i >= _upgradeData.effectValueList.Count)
|
||||
break;
|
||||
|
||||
string statName = FormatStatName(_upgradeData.effectStatList[i]);
|
||||
string op = _upgradeData.effectOpList[i];
|
||||
float value = _upgradeData.effectValueList[i];
|
||||
|
||||
// 간단한 텍스트로 표시 (EffectRow 프리팹이 없는 경우)
|
||||
var textObj = new GameObject($"Effect_{i}");
|
||||
textObj.transform.SetParent(_effectRowsParent);
|
||||
var text = textObj.AddComponent<TextMeshProUGUI>();
|
||||
text.text = $"{statName}: {FormatValue(op, value)}";
|
||||
text.fontSize = 24;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateReqChips()
|
||||
{
|
||||
if (_reqChipsParent == null || _upgradeData == null) return;
|
||||
|
||||
// 기존 자식 제거
|
||||
foreach (Transform child in _reqChipsParent)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
// 선행조건이 없으면 표시하지 않음
|
||||
if (_upgradeData.requireUpgradeId.Count == 0) return;
|
||||
|
||||
foreach (int reqId in _upgradeData.requireUpgradeId)
|
||||
{
|
||||
var database = UpgradeDatabase.Instance;
|
||||
var reqUpgrade = database?.GetUpgradeById(reqId);
|
||||
if (reqUpgrade == null) continue;
|
||||
|
||||
// 간단한 텍스트로 표시 (ReqChip 프리팹이 없는 경우)
|
||||
var textObj = new GameObject($"Req_{reqId}");
|
||||
textObj.transform.SetParent(_reqChipsParent);
|
||||
var text = textObj.AddComponent<TextMeshProUGUI>();
|
||||
text.text = $"Requires: {reqUpgrade.memo}";
|
||||
text.fontSize = 20;
|
||||
text.color = Color.yellow;
|
||||
}
|
||||
}
|
||||
|
||||
private string FormatStatName(string stat)
|
||||
{
|
||||
return stat switch
|
||||
{
|
||||
"player_max_hp" => "Max HP",
|
||||
"player_atk_damage" => "ATK Damage",
|
||||
"player_capacity" => "Capacity",
|
||||
"player_manpower" => "Manpower",
|
||||
"player_move_speed" => "Move Speed",
|
||||
"player_sight" => "Sight",
|
||||
"player_atk_range" => "ATK Range",
|
||||
_ => stat
|
||||
};
|
||||
}
|
||||
|
||||
private string FormatValue(string op, float value)
|
||||
{
|
||||
return op switch
|
||||
{
|
||||
"add" => $"+{value}",
|
||||
"mul" => $"x{value}",
|
||||
"set" => $"{value}",
|
||||
_ => $"{value}"
|
||||
};
|
||||
}
|
||||
|
||||
private void OnPurchaseClicked()
|
||||
{
|
||||
if (_upgradeData == null || _parentPopup == null) return;
|
||||
if (_isOwned || !_canPurchase) return;
|
||||
|
||||
_parentPopup.RequestPurchase(_upgradeData.id);
|
||||
}
|
||||
|
||||
// IPointerClickHandler 구현 - 직접 클릭 감지
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
OnPurchaseClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UI/UpgradeListItem.cs.meta
Normal file
2
Assets/Scripts/UI/UpgradeListItem.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb93da116066efb4f95f04229c90a069
|
||||
175
Assets/Scripts/UI/UpgradeListPopup.cs
Normal file
175
Assets/Scripts/UI/UpgradeListPopup.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.UI;
|
||||
using Northbound.Data;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 업그레이드 목록 팝업 UI 컨트롤러
|
||||
/// </summary>
|
||||
public class UpgradeListPopup : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private Transform _contentParent;
|
||||
[SerializeField] private GameObject _listItemPrefab;
|
||||
[SerializeField] private TextMeshProUGUI _manaText;
|
||||
[SerializeField] private Button _closeButton;
|
||||
|
||||
private PlayerUpgradeManager _playerUpgradeManager;
|
||||
private PlayerStats _playerStats;
|
||||
private List<UpgradeListItem> _listItems = new List<UpgradeListItem>();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_closeButton != null)
|
||||
{
|
||||
_closeButton.onClick.AddListener(Close);
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// ESC 키로 닫기
|
||||
if (Keyboard.current != null && Keyboard.current.escapeKey.wasPressedThisFrame)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
// 마나 텍스트 업데이트
|
||||
UpdateManaDisplay();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 팝업 초기화
|
||||
/// </summary>
|
||||
public void Initialize(PlayerUpgradeManager upgradeManager, PlayerStats playerStats)
|
||||
{
|
||||
_playerUpgradeManager = upgradeManager;
|
||||
_playerStats = playerStats;
|
||||
|
||||
RefreshList();
|
||||
UpdateManaDisplay();
|
||||
|
||||
gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 업그레이드 목록 갱신
|
||||
/// </summary>
|
||||
public void RefreshList()
|
||||
{
|
||||
// 기존 아이템 제거
|
||||
ClearList();
|
||||
|
||||
if (_playerUpgradeManager == null) return;
|
||||
|
||||
var database = UpgradeDatabase.Instance;
|
||||
if (database == null)
|
||||
{
|
||||
Debug.LogWarning("[UpgradeListPopup] UpgradeDatabase를 찾을 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 개인 업그레이드 목록 가져오기
|
||||
var allUpgrades = database.GetPersonalUpgrades();
|
||||
var ownedIds = _playerUpgradeManager.GetOwnedUpgradeIdSet();
|
||||
|
||||
foreach (var upgrade in allUpgrades)
|
||||
{
|
||||
if (upgrade == null) continue;
|
||||
|
||||
bool isOwned = ownedIds.Contains(upgrade.id);
|
||||
bool prerequisitesMet = database.ArePrerequisitesMet(upgrade.id, ownedIds);
|
||||
|
||||
// 이미 구매했거나, 선행조건이 충족된 항목만 표시
|
||||
if (!isOwned && !prerequisitesMet) continue;
|
||||
|
||||
bool canPurchase = !isOwned && prerequisitesMet;
|
||||
CreateListItem(upgrade, isOwned, canPurchase);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 리스트 아이템 생성
|
||||
/// </summary>
|
||||
private void CreateListItem(UpgradeData upgrade, bool isOwned, bool canPurchase)
|
||||
{
|
||||
if (_listItemPrefab == null || _contentParent == null) return;
|
||||
|
||||
GameObject itemObj = Instantiate(_listItemPrefab, _contentParent);
|
||||
var listItem = itemObj.GetComponent<UpgradeListItem>();
|
||||
|
||||
if (listItem != null)
|
||||
{
|
||||
listItem.Setup(upgrade, isOwned, canPurchase, this);
|
||||
_listItems.Add(listItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
// UpgradeListItem 컴포넌트가 없으면 추가
|
||||
listItem = itemObj.AddComponent<UpgradeListItem>();
|
||||
listItem.Setup(upgrade, isOwned, canPurchase, this);
|
||||
_listItems.Add(listItem);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 리스트 비우기
|
||||
/// </summary>
|
||||
private void ClearList()
|
||||
{
|
||||
foreach (var item in _listItems)
|
||||
{
|
||||
if (item != null && item.gameObject != null)
|
||||
{
|
||||
Destroy(item.gameObject);
|
||||
}
|
||||
}
|
||||
_listItems.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 업그레이드 구매 요청
|
||||
/// </summary>
|
||||
public void RequestPurchase(int upgradeId)
|
||||
{
|
||||
if (_playerUpgradeManager == null) return;
|
||||
|
||||
_playerUpgradeManager.RequestPurchaseUpgrade(upgradeId);
|
||||
|
||||
// 구매 후 목록 갱신
|
||||
RefreshList();
|
||||
UpdateManaDisplay();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 마나 표시 업데이트
|
||||
/// </summary>
|
||||
private void UpdateManaDisplay()
|
||||
{
|
||||
if (_manaText == null) return;
|
||||
|
||||
var core = CoreResourceManager.Instance?.mainCore;
|
||||
if (core != null)
|
||||
{
|
||||
_manaText.text = $"MANA: {core.TotalResources}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 팝업 닫기
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ClearList();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UI/UpgradeListPopup.cs.meta
Normal file
2
Assets/Scripts/UI/UpgradeListPopup.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d9a732b420252f48bfa530b3f90d50b
|
||||
Reference in New Issue
Block a user