using System; using System.Collections.Generic; using UnityEngine; public class QuestManager : MonoBehaviour { public static QuestManager Instance { get; private set; } [SerializeField] private List availableQuests = new List(); [SerializeField] private List activeQuests = new List(); [SerializeField] private List completedQuests = new List(); [SerializeField] private List failedQuests = new List(); public IReadOnlyList AvailableQuests => availableQuests; public IReadOnlyList ActiveQuests => activeQuests; public IReadOnlyList CompletedQuests => completedQuests; public IReadOnlyList FailedQuests => failedQuests; public event Action OnQuestsChanged; private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; } public void SetAvailableQuests(IEnumerable 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 completedNow = new List(); 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); } } }