using System.Collections.Generic; using OTGIntegrated.Save; using OTGIntegrated.Stats; using UnityEngine; using UnityEngine.UI; namespace OTGIntegrated.Talents { public sealed class TalentTreeUI : MonoBehaviour { public GameObject panelRoot; public RectTransform nodesRoot; public RectTransform lineRoot; public TalentTooltipUI tooltip; public Text titleText; public Text pointsText; public Text statsText; public Button saveButton; public Button loadButton; public Button resetButton; public Button closeButton; public Font uiFont; private TalentManager talentManager; private PlayerStats playerStats; private readonly List nodes = new List(); private readonly List spawnedLines = new List(); public void Initialize(TalentManager manager, PlayerStats stats) { talentManager = manager; playerStats = stats; if (talentManager != null) { talentManager.OnTalentStateChanged -= Refresh; talentManager.OnTalentStateChanged += Refresh; } if (playerStats != null) { playerStats.OnStatsChanged -= Refresh; playerStats.OnStatsChanged += Refresh; } if (saveButton != null) saveButton.onClick.AddListener(() => FindObjectOfType()?.Save()); if (loadButton != null) loadButton.onClick.AddListener(() => FindObjectOfType()?.Load()); if (resetButton != null) resetButton.onClick.AddListener(() => FindObjectOfType()?.ResetSave()); if (closeButton != null) closeButton.onClick.AddListener(() => FindObjectOfType()?.CloseCurrentWindow()); BuildTree(); Refresh(); } private void OnEnable() { Refresh(); } private void OnDestroy() { if (talentManager != null) { talentManager.OnTalentStateChanged -= Refresh; } if (playerStats != null) { playerStats.OnStatsChanged -= Refresh; } } public void BuildTree() { ClearTree(); if (talentManager == null || nodesRoot == null) { return; } Dictionary lookup = new Dictionary(); for (int i = 0; i < talentManager.allTalents.Count; i++) { TalentData talent = talentManager.allTalents[i]; if (talent == null) { continue; } TalentNodeUI node = CreateNode(talent); nodes.Add(node); lookup[talent] = node; } for (int i = 0; i < talentManager.allTalents.Count; i++) { TalentData talent = talentManager.allTalents[i]; if (talent == null || talent.prerequisites == null || !lookup.TryGetValue(talent, out TalentNodeUI node)) { continue; } for (int p = 0; p < talent.prerequisites.Length; p++) { TalentData prerequisite = talent.prerequisites[p]; if (prerequisite != null && lookup.TryGetValue(prerequisite, out TalentNodeUI prerequisiteNode)) { CreateLine(prerequisiteNode.GetComponent().anchoredPosition, node.GetComponent().anchoredPosition); } } } } public void Refresh() { if (titleText != null) { titleText.text = "Дерево талантов"; } if (pointsText != null) { int points = talentManager != null ? talentManager.availableTalentPoints : 0; pointsText.text = $"Очки талантов: {points}"; } if (statsText != null) { statsText.text = playerStats == null ? "Характеристики недоступны" : $"Характеристики\nHP: {Mathf.RoundToInt(playerStats.CurrentHealth)} / {Mathf.RoundToInt(playerStats.MaxHealth)}\nMana: {Mathf.RoundToInt(playerStats.CurrentMana)} / {Mathf.RoundToInt(playerStats.MaxMana)}\nDamage: {playerStats.Damage:0.#}\nMove: {playerStats.MoveSpeed:0.#}\nCrit: {playerStats.CriticalChance:P0}\nCooldown: {playerStats.CooldownReduction:P0}"; } for (int i = 0; i < nodes.Count; i++) { if (nodes[i] != null) { nodes[i].Refresh(); } } } private TalentNodeUI CreateNode(TalentData talent) { GameObject root = new GameObject(talent.displayName, typeof(RectTransform), typeof(Image), typeof(Button), typeof(TalentNodeUI)); root.transform.SetParent(nodesRoot, false); RectTransform rect = root.GetComponent(); rect.sizeDelta = new Vector2(160f, 76f); rect.anchorMin = new Vector2(0.5f, 0.5f); rect.anchorMax = new Vector2(0.5f, 0.5f); rect.anchoredPosition = talent.treePosition; Text title = CreateText(root.transform, "Title", talent.displayName, 15, TextAnchor.MiddleCenter); title.rectTransform.anchorMin = new Vector2(0.08f, 0.25f); title.rectTransform.anchorMax = new Vector2(0.92f, 0.9f); title.rectTransform.offsetMin = Vector2.zero; title.rectTransform.offsetMax = Vector2.zero; Text cost = CreateText(root.transform, "Cost", talent.cost.ToString(), 14, TextAnchor.MiddleCenter); cost.rectTransform.anchorMin = new Vector2(0.78f, 0.02f); cost.rectTransform.anchorMax = new Vector2(0.98f, 0.3f); cost.rectTransform.offsetMin = Vector2.zero; cost.rectTransform.offsetMax = Vector2.zero; TalentNodeUI node = root.GetComponent(); node.background = root.GetComponent(); node.button = root.GetComponent