first commit
This commit is contained in:
@@ -0,0 +1,408 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user