using System; using System.Collections.Generic; using OTGIntegrated.Core; using OTGIntegrated.Stats; using UnityEngine; namespace OTGIntegrated.Talents { public sealed class TalentManager : MonoBehaviour { private const string TalentSourcePrefix = "Talent:"; public List allTalents = new List(); public int availableTalentPoints; [SerializeField] private List learnedTalentIds = new List(); private PlayerStats playerStats; private readonly Dictionary talentLookup = new Dictionary(StringComparer.Ordinal); public event Action OnTalentStateChanged; public IReadOnlyList LearnedTalentIds => learnedTalentIds; public void Initialize(PlayerStats stats) { playerStats = stats; BuildLookup(); RebuildAllModifiers(); RaiseChanged(); } private void Awake() { if (playerStats == null) { playerStats = GetComponent(); } BuildLookup(); } public bool IsLearned(TalentData talent) { return IsValidTalent(talent) && learnedTalentIds.Contains(talent.talentId); } public bool CanLearn(TalentData talent) { return IsValidTalent(talent) && !IsLearned(talent) && availableTalentPoints >= Mathf.Max(0, talent.cost) && ArePrerequisitesLearned(talent); } public bool ArePrerequisitesLearned(TalentData talent) { if (!IsValidTalent(talent)) { return false; } if (talent.prerequisites == null || talent.prerequisites.Length == 0) { return true; } for (int i = 0; i < talent.prerequisites.Length; i++) { TalentData prerequisite = talent.prerequisites[i]; if (!IsValidTalent(prerequisite) || !IsLearned(prerequisite)) { return false; } } return true; } public bool LearnTalent(TalentData talent) { if (!IsValidTalent(talent)) { GameEvents.RaiseNotification("Некорректный талант"); return false; } if (!CanLearn(talent)) { GameEvents.RaiseNotification(GetBlockingReason(talent)); return false; } availableTalentPoints -= Mathf.Max(0, talent.cost); learnedTalentIds.Add(talent.talentId); ApplyTalentModifiers(talent); GameEvents.RaiseTalentLearned(talent); GameEvents.RaiseNotification($"Изучен талант: {talent.displayName}"); RaiseChanged(); return true; } public void AddTalentPoints(int amount) { if (amount <= 0) { return; } availableTalentPoints = Mathf.Max(0, availableTalentPoints + amount); GameEvents.RaiseNotification(amount == 1 ? "Получено очко талантов" : $"Получено очков талантов: {amount}"); RaiseChanged(); } public void SetTalentPoints(int points) { availableTalentPoints = Mathf.Max(0, points); RaiseChanged(); } public TalentData GetTalentById(string talentId) { BuildLookup(); if (string.IsNullOrWhiteSpace(talentId)) { return null; } talentLookup.TryGetValue(talentId, out TalentData talent); return talent; } public string GetBlockingReason(TalentData talent) { if (!IsValidTalent(talent)) { return "Некорректные данные таланта"; } if (IsLearned(talent)) { return "Талант уже изучен"; } if (!ArePrerequisitesLearned(talent)) { return GetMissingPrerequisiteText(talent); } if (availableTalentPoints < Mathf.Max(0, talent.cost)) { return "Недостаточно очков талантов"; } return "Доступно"; } public void LoadProgress(int points, List learnedIds) { BuildLookup(); availableTalentPoints = Mathf.Max(0, points); learnedTalentIds.Clear(); if (learnedIds != null) { for (int i = 0; i < learnedIds.Count; i++) { string id = learnedIds[i]; if (!string.IsNullOrWhiteSpace(id) && talentLookup.ContainsKey(id) && !learnedTalentIds.Contains(id)) { learnedTalentIds.Add(id); } } } RebuildAllModifiers(); RaiseChanged(); } public void ResetTalents() { learnedTalentIds.Clear(); availableTalentPoints = 0; RebuildAllModifiers(); RaiseChanged(); } public void RebuildAllModifiers() { if (playerStats == null) { return; } playerStats.RemoveModifiersBySourcePrefix(TalentSourcePrefix); for (int i = 0; i < allTalents.Count; i++) { TalentData talent = allTalents[i]; if (IsLearned(talent)) { ApplyTalentModifiers(talent); } } playerStats.RecalculateStats(); } private void ApplyTalentModifiers(TalentData talent) { if (!IsValidTalent(talent) || playerStats == null || talent.modifiers == null) { return; } string sourceId = TalentSourcePrefix + talent.talentId; playerStats.RemoveModifiersBySource(sourceId); for (int i = 0; i < talent.modifiers.Length; i++) { StatModifier modifier = talent.modifiers[i]; modifier.sourceId = sourceId; playerStats.AddModifier(modifier); } } private void BuildLookup() { talentLookup.Clear(); for (int i = 0; i < allTalents.Count; i++) { TalentData talent = allTalents[i]; if (IsValidTalent(talent)) { talentLookup[talent.talentId] = talent; } } } private bool IsValidTalent(TalentData talent) { return talent != null && !string.IsNullOrWhiteSpace(talent.talentId); } private string GetMissingPrerequisiteText(TalentData talent) { if (talent.prerequisites == null) { return "Требования не выполнены"; } for (int i = 0; i < talent.prerequisites.Length; i++) { TalentData prerequisite = talent.prerequisites[i]; if (!IsValidTalent(prerequisite) || !IsLearned(prerequisite)) { return prerequisite != null ? $"Требуется: {prerequisite.displayName}" : "Требуется другой талант"; } } return "Требования не выполнены"; } private void RaiseChanged() { GameEvents.RaiseTalentPointChanged(availableTalentPoints); OnTalentStateChanged?.Invoke(); } } }