232 lines
8.7 KiB
C#
232 lines
8.7 KiB
C#
|
|
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<TalentNodeUI> nodes = new List<TalentNodeUI>();
|
||
|
|
private readonly List<GameObject> spawnedLines = new List<GameObject>();
|
||
|
|
|
||
|
|
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<SaveSystem>()?.Save());
|
||
|
|
if (loadButton != null) loadButton.onClick.AddListener(() => FindObjectOfType<SaveSystem>()?.Load());
|
||
|
|
if (resetButton != null) resetButton.onClick.AddListener(() => FindObjectOfType<SaveSystem>()?.ResetSave());
|
||
|
|
if (closeButton != null) closeButton.onClick.AddListener(() => FindObjectOfType<OTGIntegrated.UI.UIManager>()?.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<TalentData, TalentNodeUI> lookup = new Dictionary<TalentData, TalentNodeUI>();
|
||
|
|
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<RectTransform>().anchoredPosition, node.GetComponent<RectTransform>().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<RectTransform>();
|
||
|
|
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<TalentNodeUI>();
|
||
|
|
node.background = root.GetComponent<Image>();
|
||
|
|
node.button = root.GetComponent<Button>();
|
||
|
|
node.titleText = title;
|
||
|
|
node.costText = cost;
|
||
|
|
node.Initialize(talent, talentManager, tooltip, uiFont);
|
||
|
|
return node;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void CreateLine(Vector2 start, Vector2 end)
|
||
|
|
{
|
||
|
|
if (lineRoot == null)
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
GameObject line = new GameObject("TalentLine", typeof(RectTransform), typeof(Image));
|
||
|
|
line.transform.SetParent(lineRoot, false);
|
||
|
|
spawnedLines.Add(line);
|
||
|
|
Image image = line.GetComponent<Image>();
|
||
|
|
image.color = new Color(0.78f, 0.62f, 0.26f, 0.55f);
|
||
|
|
|
||
|
|
RectTransform rect = line.GetComponent<RectTransform>();
|
||
|
|
Vector2 delta = end - start;
|
||
|
|
rect.anchorMin = new Vector2(0.5f, 0.5f);
|
||
|
|
rect.anchorMax = new Vector2(0.5f, 0.5f);
|
||
|
|
rect.sizeDelta = new Vector2(delta.magnitude, 3f);
|
||
|
|
rect.anchoredPosition = start + delta * 0.5f;
|
||
|
|
rect.localRotation = Quaternion.Euler(0f, 0f, Mathf.Atan2(delta.y, delta.x) * Mathf.Rad2Deg);
|
||
|
|
}
|
||
|
|
|
||
|
|
private Text CreateText(Transform parent, string name, string value, int fontSize, TextAnchor anchor)
|
||
|
|
{
|
||
|
|
GameObject textObject = new GameObject(name, typeof(RectTransform), typeof(Text));
|
||
|
|
textObject.transform.SetParent(parent, false);
|
||
|
|
Text text = textObject.GetComponent<Text>();
|
||
|
|
text.font = uiFont != null ? uiFont : Resources.GetBuiltinResource<Font>("Arial.ttf");
|
||
|
|
text.fontSize = fontSize;
|
||
|
|
text.alignment = anchor;
|
||
|
|
text.color = new Color(0.95f, 0.94f, 0.9f, 1f);
|
||
|
|
text.text = value;
|
||
|
|
text.resizeTextForBestFit = true;
|
||
|
|
text.resizeTextMinSize = 10;
|
||
|
|
text.resizeTextMaxSize = fontSize;
|
||
|
|
return text;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void ClearTree()
|
||
|
|
{
|
||
|
|
for (int i = nodesRoot != null ? nodesRoot.childCount - 1 : -1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
Destroy(nodesRoot.GetChild(i).gameObject);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (int i = 0; i < spawnedLines.Count; i++)
|
||
|
|
{
|
||
|
|
if (spawnedLines[i] != null)
|
||
|
|
{
|
||
|
|
Destroy(spawnedLines[i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
nodes.Clear();
|
||
|
|
spawnedLines.Clear();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|