using System; using System.Collections.Generic; using OTGIntegrated.Core; using OTGIntegrated.Items; using OTGIntegrated.Stats; using OTGIntegrated.Talents; using UnityEngine; namespace OTGIntegrated.Quests { [Serializable] public sealed class QuestState { public QuestData questData; public QuestStatus status; public List progress = new List(); public QuestState(QuestData quest) { questData = quest; status = QuestStatus.Active; int count = quest != null && quest.goals != null ? quest.goals.Count : 0; for (int i = 0; i < count; i++) { progress.Add(0); } } public int GetProgress(int goalIndex) { return goalIndex >= 0 && goalIndex < progress.Count ? progress[goalIndex] : 0; } public void SetProgress(int goalIndex, int amount) { if (questData == null || questData.goals == null || goalIndex < 0 || goalIndex >= questData.goals.Count) { return; } while (progress.Count < questData.goals.Count) { progress.Add(0); } int required = Mathf.Max(1, questData.goals[goalIndex].requiredAmount); progress[goalIndex] = Mathf.Clamp(amount, 0, required); } public void AddProgress(int goalIndex, int amount) { SetProgress(goalIndex, GetProgress(goalIndex) + Mathf.Max(0, amount)); } public bool IsGoalComplete(int goalIndex) { if (questData == null || questData.goals == null || goalIndex < 0 || goalIndex >= questData.goals.Count) { return false; } return GetProgress(goalIndex) >= Mathf.Max(1, questData.goals[goalIndex].requiredAmount); } public bool AreAllGoalsComplete() { if (questData == null || questData.goals == null || questData.goals.Count == 0) { return false; } for (int i = 0; i < questData.goals.Count; i++) { if (!IsGoalComplete(i)) { return false; } } return true; } } [Serializable] public sealed class QuestSaveEntry { public string questId; public QuestStatus status; public List progress = new List(); } public sealed class QuestManager : MonoBehaviour { public List allQuests = new List(); [SerializeField] private List activeQuests = new List(); [SerializeField] private List completedQuests = new List(); private OTGIntegrated.Inventory.Inventory inventory; private LevelSystem levelSystem; private TalentManager talentManager; private readonly Dictionary questLookup = new Dictionary(StringComparer.Ordinal); public event Action OnQuestsChanged; public IReadOnlyList ActiveQuests => activeQuests; public IReadOnlyList CompletedQuests => completedQuests; public void Initialize(OTGIntegrated.Inventory.Inventory inventory, LevelSystem levelSystem, TalentManager talentManager) { this.inventory = inventory; this.levelSystem = levelSystem; this.talentManager = talentManager; BuildLookup(); OnQuestsChanged?.Invoke(); } private void OnEnable() { GameEvents.OnItemAdded += HandleItemAdded; GameEvents.OnEnemyKilled += HandleEnemyKilled; } private void OnDisable() { GameEvents.OnItemAdded -= HandleItemAdded; GameEvents.OnEnemyKilled -= HandleEnemyKilled; } public bool StartQuest(QuestData quest) { if (quest == null || IsQuestActive(quest) || IsQuestCompleted(quest)) { return false; } QuestState state = new QuestState(quest); activeQuests.Add(state); SynchronizeInventoryGoals(state); UpdateReadyState(state); GameEvents.RaiseQuestStarted(quest); GameEvents.RaiseNotification($"Квест получен: {quest.displayName}"); OnQuestsChanged?.Invoke(); return true; } public bool TryCompleteQuest(QuestData quest, string npcId) { QuestState state = GetActiveState(quest); if (state == null) { return false; } if (!string.IsNullOrWhiteSpace(quest.turnInNpcId) && quest.turnInNpcId != npcId) { return false; } if (state.status != QuestStatus.ReadyToComplete && !state.AreAllGoalsComplete()) { GameEvents.RaiseNotification("Цели квеста ещё не выполнены"); return false; } if (!ConsumeCollectGoals(quest)) { GameEvents.RaiseNotification("Не хватает предметов для сдачи квеста"); return false; } state.status = QuestStatus.Completed; activeQuests.Remove(state); completedQuests.Add(state); if (quest.rewardExperience > 0 && levelSystem != null) { levelSystem.AddExperience(quest.rewardExperience); } if (quest.rewardTalentPoints > 0 && talentManager != null) { talentManager.AddTalentPoints(quest.rewardTalentPoints); } if (quest.rewardItems != null && inventory != null) { for (int i = 0; i < quest.rewardItems.Count; i++) { QuestRewardItem reward = quest.rewardItems[i]; if (reward != null && reward.item != null && reward.amount > 0) { inventory.AddItem(reward.item, reward.amount); } } } GameEvents.RaiseQuestCompleted(quest); GameEvents.RaiseNotification($"Квест выполнен: {quest.displayName}"); OnQuestsChanged?.Invoke(); return true; } public void NotifyNpcTalked(string npcId) { if (!string.IsNullOrWhiteSpace(npcId)) { UpdateProgress(QuestGoalType.TalkToNPC, npcId, 1); } } public bool IsQuestActive(QuestData quest) { return GetActiveState(quest) != null; } public bool IsQuestCompleted(QuestData quest) { return quest != null && completedQuests.Exists(state => state.questData == quest); } public bool IsQuestReady(QuestData quest) { QuestState state = GetActiveState(quest); return state != null && state.status == QuestStatus.ReadyToComplete; } public QuestState GetActiveState(QuestData quest) { return quest == null ? null : activeQuests.Find(state => state.questData == quest); } public QuestData GetQuestById(string questId) { BuildLookup(); if (string.IsNullOrWhiteSpace(questId)) { return null; } questLookup.TryGetValue(questId, out QuestData quest); return quest; } public string GetPrimaryTrackerText() { QuestState state = activeQuests.Count > 0 ? activeQuests[0] : null; if (state == null || state.questData == null) { return "Нет активных квестов"; } return $"{state.questData.displayName}\n{GetStateProgressText(state)}"; } public string GetStateProgressText(QuestState state) { if (state == null || state.questData == null || state.questData.goals == null) { return string.Empty; } System.Text.StringBuilder builder = new System.Text.StringBuilder(); for (int i = 0; i < state.questData.goals.Count; i++) { QuestGoal goal = state.questData.goals[i]; if (goal == null) { continue; } builder.Append(goal.GetDisplayName()); builder.Append(": "); builder.Append(state.GetProgress(i)); builder.Append(" / "); builder.Append(goal.requiredAmount); if (i < state.questData.goals.Count - 1) { builder.AppendLine(); } } if (state.status == QuestStatus.ReadyToComplete) { builder.AppendLine(); builder.Append("Вернитесь к NPC"); } return builder.ToString(); } public List SaveData() { List entries = new List(); AppendSaveEntries(activeQuests, entries); AppendSaveEntries(completedQuests, entries); return entries; } public void LoadData(List entries) { BuildLookup(); activeQuests.Clear(); completedQuests.Clear(); if (entries != null) { for (int i = 0; i < entries.Count; i++) { QuestSaveEntry entry = entries[i]; if (entry == null || string.IsNullOrWhiteSpace(entry.questId)) { continue; } QuestData quest = GetQuestById(entry.questId); if (quest == null) { continue; } QuestState state = new QuestState(quest); state.status = entry.status; for (int p = 0; entry.progress != null && p < entry.progress.Count && p < state.progress.Count; p++) { state.SetProgress(p, entry.progress[p]); } if (state.status == QuestStatus.Completed) { completedQuests.Add(state); } else { activeQuests.Add(state); } } } OnQuestsChanged?.Invoke(); } private void HandleItemAdded(ItemData item, int amount) { if (item == null || amount <= 0) { return; } UpdateProgress(QuestGoalType.CollectItem, item.itemId, amount); UpdateProgress(QuestGoalType.CraftItem, item.itemId, amount); } private void HandleEnemyKilled(string enemyId) { if (!string.IsNullOrWhiteSpace(enemyId)) { UpdateProgress(QuestGoalType.KillEnemy, enemyId, 1); } } private void UpdateProgress(QuestGoalType goalType, string targetKey, int amount) { if (string.IsNullOrWhiteSpace(targetKey) || amount <= 0) { return; } bool changed = false; for (int i = 0; i < activeQuests.Count; i++) { QuestState state = activeQuests[i]; QuestData quest = state.questData; if (state.status == QuestStatus.ReadyToComplete || quest == null || quest.goals == null) { continue; } for (int goalIndex = 0; goalIndex < quest.goals.Count; goalIndex++) { QuestGoal goal = quest.goals[goalIndex]; if (goal == null || goal.goalType != goalType || goal.GetTargetKey() != targetKey || state.IsGoalComplete(goalIndex)) { continue; } state.AddProgress(goalIndex, amount); changed = true; GameEvents.RaiseQuestProgressChanged(quest); GameEvents.RaiseNotification($"{quest.displayName}: {goal.GetDisplayName()} {state.GetProgress(goalIndex)}/{goal.requiredAmount}"); } if (UpdateReadyState(state)) { changed = true; } } if (changed) { OnQuestsChanged?.Invoke(); } } private bool UpdateReadyState(QuestState state) { if (state == null || state.status != QuestStatus.Active || !state.AreAllGoalsComplete()) { return false; } state.status = QuestStatus.ReadyToComplete; GameEvents.RaiseQuestReadyToComplete(state.questData); GameEvents.RaiseNotification($"Квест готов к сдаче: {state.questData.displayName}"); return true; } private void SynchronizeInventoryGoals(QuestState state) { if (state == null || state.questData == null || state.questData.goals == null || inventory == null) { return; } for (int i = 0; i < state.questData.goals.Count; i++) { QuestGoal goal = state.questData.goals[i]; if (goal != null && goal.goalType == QuestGoalType.CollectItem && goal.targetItem != null) { state.SetProgress(i, inventory.GetAmount(goal.targetItem)); } } } private bool ConsumeCollectGoals(QuestData quest) { if (quest == null || quest.goals == null || inventory == null) { return true; } for (int i = 0; i < quest.goals.Count; i++) { QuestGoal goal = quest.goals[i]; if (goal != null && goal.goalType == QuestGoalType.CollectItem && goal.targetItem != null) { if (!inventory.HasItem(goal.targetItem, goal.requiredAmount)) { return false; } } } for (int i = 0; i < quest.goals.Count; i++) { QuestGoal goal = quest.goals[i]; if (goal != null && goal.goalType == QuestGoalType.CollectItem && goal.targetItem != null) { inventory.RemoveItem(goal.targetItem, goal.requiredAmount); } } return true; } private void BuildLookup() { questLookup.Clear(); for (int i = 0; i < allQuests.Count; i++) { QuestData quest = allQuests[i]; if (quest != null && !string.IsNullOrWhiteSpace(quest.questId)) { questLookup[quest.questId] = quest; } } } private void AppendSaveEntries(List states, List entries) { for (int i = 0; i < states.Count; i++) { QuestState state = states[i]; if (state == null || state.questData == null) { continue; } entries.Add(new QuestSaveEntry { questId = state.questData.questId, status = state.status, progress = new List(state.progress) }); } } } }