Files

499 lines
16 KiB
C#
Raw Permalink Normal View History

2026-06-04 23:22:13 +03:00
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<int> progress = new List<int>();
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<int> progress = new List<int>();
}
public sealed class QuestManager : MonoBehaviour
{
public List<QuestData> allQuests = new List<QuestData>();
[SerializeField] private List<QuestState> activeQuests = new List<QuestState>();
[SerializeField] private List<QuestState> completedQuests = new List<QuestState>();
private OTGIntegrated.Inventory.Inventory inventory;
private LevelSystem levelSystem;
private TalentManager talentManager;
private readonly Dictionary<string, QuestData> questLookup = new Dictionary<string, QuestData>(StringComparer.Ordinal);
public event Action OnQuestsChanged;
public IReadOnlyList<QuestState> ActiveQuests => activeQuests;
public IReadOnlyList<QuestState> 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<QuestSaveEntry> SaveData()
{
List<QuestSaveEntry> entries = new List<QuestSaveEntry>();
AppendSaveEntries(activeQuests, entries);
AppendSaveEntries(completedQuests, entries);
return entries;
}
public void LoadData(List<QuestSaveEntry> 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<QuestState> states, List<QuestSaveEntry> 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<int>(state.progress)
});
}
}
}
}