Files
OTG_5/Assets/_Scripts/QuestSystem/QuestManager.cs
T

277 lines
7.5 KiB
C#
Raw Normal View History

2026-06-04 20:14:47 +03:00
using System;
using System.Collections.Generic;
using UnityEngine;
public class QuestManager : MonoBehaviour
{
public static QuestManager Instance { get; private set; }
[SerializeField] private List<QuestData> availableQuests = new List<QuestData>();
[SerializeField] private List<QuestState> activeQuests = new List<QuestState>();
[SerializeField] private List<QuestState> completedQuests = new List<QuestState>();
[SerializeField] private List<QuestState> failedQuests = new List<QuestState>();
public IReadOnlyList<QuestData> AvailableQuests => availableQuests;
public IReadOnlyList<QuestState> ActiveQuests => activeQuests;
public IReadOnlyList<QuestState> CompletedQuests => completedQuests;
public IReadOnlyList<QuestState> FailedQuests => failedQuests;
public event Action OnQuestsChanged;
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
}
public void SetAvailableQuests(IEnumerable<QuestData> quests)
{
availableQuests.Clear();
if (quests != null)
{
availableQuests.AddRange(quests);
}
}
public bool StartQuest(QuestData quest)
{
if (quest == null || IsQuestActive(quest) || IsQuestCompleted(quest))
{
return false;
}
if (!availableQuests.Contains(quest))
{
availableQuests.Add(quest);
}
QuestState state = new QuestState(quest);
activeQuests.Add(state);
Notify($"Получен квест:\n{quest.questName}");
OnQuestsChanged?.Invoke();
return true;
}
public bool CompleteQuest(QuestData quest)
{
QuestState state = activeQuests.Find(candidate => candidate.questData == quest);
if (state == null || !state.AreAllGoalsComplete())
{
return false;
}
if (!ConsumeRequiredItems(quest))
{
Notify($"Не хватает предметов для завершения:\n{quest.questName}");
OnQuestsChanged?.Invoke();
return false;
}
state.status = QuestStatus.Completed;
activeQuests.Remove(state);
completedQuests.Add(state);
Notify($"Квест завершён:\n{quest.questName}");
if (quest.rewardItem != null && quest.rewardAmount > 0 && Inventory.Instance != null)
{
Inventory.Instance.AddItem(quest.rewardItem, quest.rewardAmount, false);
Notify($"Получена награда:\n{quest.rewardItem.itemId} x{quest.rewardAmount}");
}
OnQuestsChanged?.Invoke();
return true;
}
private bool ConsumeRequiredItems(QuestData quest)
{
if (quest == null || quest.goals == null || Inventory.Instance == null)
{
return true;
}
foreach (QuestGoal goal in quest.goals)
{
if (goal.goalType != QuestGoalType.CollectItem || goal.targetItem == null)
{
continue;
}
if (!Inventory.Instance.HasItem(goal.targetItem, goal.requiredAmount))
{
return false;
}
}
foreach (QuestGoal goal in quest.goals)
{
if (goal.goalType == QuestGoalType.CollectItem && goal.targetItem != null)
{
Inventory.Instance.RemoveItem(goal.targetItem, goal.requiredAmount);
}
}
return true;
}
public void FailQuest(QuestData quest)
{
QuestState state = activeQuests.Find(candidate => candidate.questData == quest);
if (state == null)
{
return;
}
state.status = QuestStatus.Failed;
activeQuests.Remove(state);
failedQuests.Add(state);
OnQuestsChanged?.Invoke();
}
public void OnItemCollected(ItemData item, int amount)
{
if (item == null || amount <= 0)
{
return;
}
UpdateProgress(QuestGoalType.CollectItem, item.itemId, amount);
}
public void OnEnemyKilled(string enemyType)
{
if (string.IsNullOrWhiteSpace(enemyType))
{
return;
}
UpdateProgress(QuestGoalType.KillEnemy, enemyType, 1);
}
public void OnNpcTalked(string npcId)
{
if (string.IsNullOrWhiteSpace(npcId))
{
return;
}
UpdateProgress(QuestGoalType.TalkToNPC, npcId, 1);
}
public string GetGoalProgress(QuestData quest, int goalIndex)
{
QuestState state = GetQuestState(quest);
if (state == null || quest == null || goalIndex < 0 || goalIndex >= quest.goals.Count)
{
return "0/0";
}
QuestGoal goal = quest.goals[goalIndex];
return $"{state.GetProgress(goalIndex)}/{goal.requiredAmount}";
}
public float GetQuestCompletionPercent(QuestState state)
{
if (state == null || state.questData == null || state.questData.goals.Count == 0)
{
return 0f;
}
float total = 0f;
for (int i = 0; i < state.questData.goals.Count; i++)
{
QuestGoal goal = state.questData.goals[i];
total += Mathf.Clamp01(state.GetProgress(i) / (float)Mathf.Max(1, goal.requiredAmount));
}
return total / state.questData.goals.Count;
}
public QuestState GetQuestState(QuestData quest)
{
QuestState state = activeQuests.Find(candidate => candidate.questData == quest);
if (state != null)
{
return state;
}
state = completedQuests.Find(candidate => candidate.questData == quest);
if (state != null)
{
return state;
}
return failedQuests.Find(candidate => candidate.questData == quest);
}
private void UpdateProgress(QuestGoalType type, string targetKey, int amount)
{
bool changed = false;
List<QuestData> completedNow = new List<QuestData>();
foreach (QuestState state in activeQuests)
{
QuestData quest = state.questData;
if (quest == null || quest.goals == null)
{
continue;
}
for (int i = 0; i < quest.goals.Count; i++)
{
QuestGoal goal = quest.goals[i];
if (goal.goalType != type || goal.GetTargetKey() != targetKey || state.IsGoalComplete(i))
{
continue;
}
state.AddProgress(i, amount);
changed = true;
Notify($"Прогресс:\n{goal.GetDisplayName()} {state.GetProgress(i)}/{goal.requiredAmount}");
}
if (state.AreAllGoalsComplete())
{
completedNow.Add(quest);
}
}
foreach (QuestData quest in completedNow)
{
CompleteQuest(quest);
}
if (changed)
{
OnQuestsChanged?.Invoke();
}
}
private bool IsQuestActive(QuestData quest)
{
return activeQuests.Exists(candidate => candidate.questData == quest);
}
private bool IsQuestCompleted(QuestData quest)
{
return completedQuests.Exists(candidate => candidate.questData == quest);
}
private void Notify(string message)
{
if (NotificationUI.Instance != null)
{
NotificationUI.Instance.Show(message);
}
else
{
Debug.Log(message);
}
}
}