using System.Collections.Generic; using UnityEngine; namespace Colosseum.Passives { /// /// 패시브 트리 전체 데이터를 정의합니다. /// [CreateAssetMenu(fileName = "PassiveTree", menuName = "Colosseum/Passives/Passive Tree")] public class PassiveTreeData : ScriptableObject { [Header("기본 정보")] [SerializeField] private string treeId; [SerializeField] private string treeName; [TextArea(2, 4)] [SerializeField] private string description; [Min(0)] [SerializeField] private int initialPoints = 0; [Header("노드")] [SerializeField] private List nodes = new List(); public string TreeId => treeId; public string TreeName => treeName; public string Description => description; public int InitialPoints => initialPoints; public IReadOnlyList Nodes => nodes; public PassiveNodeData GetNodeById(string nodeId) { if (string.IsNullOrWhiteSpace(nodeId)) return null; for (int i = 0; i < nodes.Count; i++) { PassiveNodeData node = nodes[i]; if (node != null && string.Equals(node.NodeId, nodeId, System.StringComparison.Ordinal)) return node; } return null; } /// /// 선택된 노드 구성이 유효한지 검사합니다. /// public bool TryResolveSelection(IReadOnlyList selectedNodeIds, out List resolvedNodes, out string reason) { resolvedNodes = new List(); reason = string.Empty; HashSet uniqueIds = new HashSet(); int totalCost = 0; if (selectedNodeIds == null) return true; for (int i = 0; i < selectedNodeIds.Count; i++) { string nodeId = selectedNodeIds[i]; if (string.IsNullOrWhiteSpace(nodeId)) continue; if (!uniqueIds.Add(nodeId)) { reason = $"중복 노드가 포함되어 있습니다: {nodeId}"; return false; } PassiveNodeData node = GetNodeById(nodeId); if (node == null) { reason = $"트리에 없는 노드입니다: {nodeId}"; return false; } resolvedNodes.Add(node); totalCost += node.Cost; } for (int i = 0; i < resolvedNodes.Count; i++) { PassiveNodeData node = resolvedNodes[i]; IReadOnlyList prerequisiteNodes = node.PrerequisiteNodes; if (prerequisiteNodes == null) continue; for (int j = 0; j < prerequisiteNodes.Count; j++) { PassiveNodeData prerequisiteNode = prerequisiteNodes[j]; if (prerequisiteNode == null) continue; if (!uniqueIds.Contains(prerequisiteNode.NodeId)) { reason = $"{node.DisplayName} 선택에는 선행 노드 {prerequisiteNode.DisplayName} 이(가) 필요합니다."; return false; } } } if (totalCost > initialPoints) { reason = $"선택한 노드 비용이 보유 포인트를 초과합니다. Used={totalCost}, Max={initialPoints}"; return false; } return true; } public int CalculateUsedPoints(IReadOnlyList selectedNodes) { if (selectedNodes == null) return 0; int totalCost = 0; for (int i = 0; i < selectedNodes.Count; i++) { PassiveNodeData node = selectedNodes[i]; if (node == null) continue; totalCost += Mathf.Max(0, node.Cost); } return totalCost; } } }