409 lines
10 KiB
C#
409 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
public class TalentManager : MonoBehaviour
|
|
{
|
|
private const string SaveKey = "Lab07_TalentProgress";
|
|
private const string TalentSourcePrefix = "Talent:";
|
|
|
|
[Header("Data")]
|
|
public List<TalentData> allTalents = new List<TalentData>();
|
|
public PlayerStats playerStats;
|
|
|
|
[Header("Runtime Progress")]
|
|
public HashSet<string> learnedTalentIds = new HashSet<string>();
|
|
public int availableTalentPoints = 1;
|
|
public int startingTalentPoints = 1;
|
|
|
|
[Header("Feedback")]
|
|
public NotificationUI notificationUI;
|
|
|
|
private readonly Dictionary<string, TalentData> talentLookup = new Dictionary<string, TalentData>();
|
|
|
|
public event Action OnTalentStateChanged;
|
|
public event Action<string> OnNotificationRequested;
|
|
|
|
private void Awake()
|
|
{
|
|
if (playerStats == null)
|
|
{
|
|
playerStats = FindObjectOfType<PlayerStats>();
|
|
}
|
|
|
|
if (notificationUI == null)
|
|
{
|
|
notificationUI = FindObjectOfType<NotificationUI>();
|
|
}
|
|
|
|
BuildTalentLookup();
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
LoadProgress(false);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (Input.GetKeyDown(KeyCode.T))
|
|
{
|
|
AddTalentPoints(1);
|
|
}
|
|
|
|
if (Input.GetKeyDown(KeyCode.F5))
|
|
{
|
|
SaveProgress();
|
|
}
|
|
|
|
if (Input.GetKeyDown(KeyCode.F9))
|
|
{
|
|
LoadProgress();
|
|
}
|
|
|
|
if (Input.GetKeyDown(KeyCode.Backspace))
|
|
{
|
|
ResetProgress();
|
|
}
|
|
}
|
|
|
|
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 void LearnTalent(TalentData talent)
|
|
{
|
|
if (!IsValidTalent(talent))
|
|
{
|
|
Notify("Некорректный талант");
|
|
return;
|
|
}
|
|
|
|
if (IsLearned(talent))
|
|
{
|
|
Notify($"Талант уже изучен: {talent.displayName}");
|
|
return;
|
|
}
|
|
|
|
if (!ArePrerequisitesLearned(talent))
|
|
{
|
|
Notify(GetBlockingReason(talent));
|
|
return;
|
|
}
|
|
|
|
int cost = Mathf.Max(0, talent.cost);
|
|
if (availableTalentPoints < cost)
|
|
{
|
|
Notify("Недостаточно очков талантов");
|
|
return;
|
|
}
|
|
|
|
availableTalentPoints -= cost;
|
|
learnedTalentIds.Add(talent.talentId);
|
|
ApplyTalentModifiers(talent);
|
|
SaveProgress(false);
|
|
Notify($"Изучен талант: {talent.displayName}");
|
|
RaiseTalentStateChanged();
|
|
}
|
|
|
|
public void AddTalentPoints(int amount)
|
|
{
|
|
if (amount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
availableTalentPoints = Mathf.Max(0, availableTalentPoints + amount);
|
|
Notify(amount == 1 ? "Получено очко талантов" : $"Получено очков талантов: {amount}");
|
|
RaiseTalentStateChanged();
|
|
}
|
|
|
|
public void ApplyTalentModifiers(TalentData talent)
|
|
{
|
|
if (!IsValidTalent(talent) || playerStats == null || talent.modifiers == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string sourceId = GetModifierSourceId(talent);
|
|
playerStats.RemoveModifiersBySource(sourceId);
|
|
|
|
for (int i = 0; i < talent.modifiers.Length; i++)
|
|
{
|
|
StatModifier modifier = talent.modifiers[i];
|
|
modifier.sourceId = sourceId;
|
|
playerStats.AddModifier(modifier);
|
|
}
|
|
}
|
|
|
|
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();
|
|
RaiseTalentStateChanged();
|
|
}
|
|
|
|
public void SaveProgress()
|
|
{
|
|
SaveProgress(true);
|
|
}
|
|
|
|
public void LoadProgress()
|
|
{
|
|
LoadProgress(true);
|
|
}
|
|
|
|
public void ResetProgress()
|
|
{
|
|
PlayerPrefs.DeleteKey(SaveKey);
|
|
ResetRuntimeToDefaults();
|
|
RebuildAllModifiers();
|
|
Notify("Прогресс сброшен");
|
|
RaiseTalentStateChanged();
|
|
}
|
|
|
|
public int GetAvailableTalentPoints()
|
|
{
|
|
return availableTalentPoints;
|
|
}
|
|
|
|
public List<TalentData> GetLearnedTalents()
|
|
{
|
|
BuildTalentLookup();
|
|
return allTalents.Where(IsLearned).ToList();
|
|
}
|
|
|
|
public string GetBlockingReason(TalentData talent)
|
|
{
|
|
if (!IsValidTalent(talent))
|
|
{
|
|
return "Некорректные данные таланта";
|
|
}
|
|
|
|
if (IsLearned(talent))
|
|
{
|
|
return "Талант уже изучен";
|
|
}
|
|
|
|
if (talent.prerequisites != null)
|
|
{
|
|
for (int i = 0; i < talent.prerequisites.Length; i++)
|
|
{
|
|
TalentData prerequisite = talent.prerequisites[i];
|
|
if (!IsValidTalent(prerequisite))
|
|
{
|
|
return "Некорректное требование таланта";
|
|
}
|
|
|
|
if (!IsLearned(prerequisite))
|
|
{
|
|
return $"Требуется талант: {prerequisite.displayName}";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (availableTalentPoints < Mathf.Max(0, talent.cost))
|
|
{
|
|
return "Недостаточно очков талантов";
|
|
}
|
|
|
|
return "Доступно";
|
|
}
|
|
|
|
public TalentData GetTalentById(string talentId)
|
|
{
|
|
BuildTalentLookup();
|
|
if (string.IsNullOrWhiteSpace(talentId))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
talentLookup.TryGetValue(talentId, out TalentData talent);
|
|
return talent;
|
|
}
|
|
|
|
private void SaveProgress(bool notify)
|
|
{
|
|
TalentSaveData saveData = new TalentSaveData
|
|
{
|
|
availableTalentPoints = Mathf.Max(0, availableTalentPoints),
|
|
learnedTalentIds = learnedTalentIds
|
|
.Where(id => !string.IsNullOrWhiteSpace(id))
|
|
.OrderBy(id => id, StringComparer.Ordinal)
|
|
.ToList()
|
|
};
|
|
|
|
PlayerPrefs.SetString(SaveKey, JsonUtility.ToJson(saveData));
|
|
PlayerPrefs.Save();
|
|
|
|
if (notify)
|
|
{
|
|
Notify("Прогресс сохранён");
|
|
}
|
|
}
|
|
|
|
private void LoadProgress(bool notify)
|
|
{
|
|
BuildTalentLookup();
|
|
learnedTalentIds.Clear();
|
|
|
|
if (PlayerPrefs.HasKey(SaveKey))
|
|
{
|
|
try
|
|
{
|
|
TalentSaveData saveData = JsonUtility.FromJson<TalentSaveData>(PlayerPrefs.GetString(SaveKey));
|
|
availableTalentPoints = Mathf.Max(0, saveData != null ? saveData.availableTalentPoints : startingTalentPoints);
|
|
|
|
if (saveData != null && saveData.learnedTalentIds != null)
|
|
{
|
|
for (int i = 0; i < saveData.learnedTalentIds.Count; i++)
|
|
{
|
|
string talentId = saveData.learnedTalentIds[i];
|
|
if (talentLookup.ContainsKey(talentId))
|
|
{
|
|
learnedTalentIds.Add(talentId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Debug.LogWarning($"Talent progress was corrupted and will be reset. {exception.Message}");
|
|
ResetRuntimeToDefaults();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ResetRuntimeToDefaults();
|
|
}
|
|
|
|
EnsureDefaultTalentsLearned();
|
|
RebuildAllModifiers();
|
|
|
|
if (notify)
|
|
{
|
|
Notify("Прогресс загружен");
|
|
}
|
|
|
|
RaiseTalentStateChanged();
|
|
}
|
|
|
|
private void ResetRuntimeToDefaults()
|
|
{
|
|
learnedTalentIds.Clear();
|
|
availableTalentPoints = Mathf.Max(0, startingTalentPoints);
|
|
EnsureDefaultTalentsLearned();
|
|
}
|
|
|
|
private void EnsureDefaultTalentsLearned()
|
|
{
|
|
for (int i = 0; i < allTalents.Count; i++)
|
|
{
|
|
TalentData talent = allTalents[i];
|
|
if (IsValidTalent(talent) && talent.unlockedByDefault)
|
|
{
|
|
learnedTalentIds.Add(talent.talentId);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void BuildTalentLookup()
|
|
{
|
|
talentLookup.Clear();
|
|
for (int i = 0; i < allTalents.Count; i++)
|
|
{
|
|
TalentData talent = allTalents[i];
|
|
if (!IsValidTalent(talent))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (talentLookup.ContainsKey(talent.talentId))
|
|
{
|
|
Debug.LogWarning($"Duplicate talent id ignored: {talent.talentId}");
|
|
continue;
|
|
}
|
|
|
|
talentLookup.Add(talent.talentId, talent);
|
|
}
|
|
}
|
|
|
|
private bool IsValidTalent(TalentData talent)
|
|
{
|
|
return talent != null && !string.IsNullOrWhiteSpace(talent.talentId);
|
|
}
|
|
|
|
private string GetModifierSourceId(TalentData talent)
|
|
{
|
|
return TalentSourcePrefix + talent.talentId;
|
|
}
|
|
|
|
private void RaiseTalentStateChanged()
|
|
{
|
|
OnTalentStateChanged?.Invoke();
|
|
}
|
|
|
|
private void Notify(string message)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(message))
|
|
{
|
|
return;
|
|
}
|
|
|
|
OnNotificationRequested?.Invoke(message);
|
|
if (notificationUI != null)
|
|
{
|
|
notificationUI.Show(message);
|
|
}
|
|
}
|
|
}
|