499 lines
16 KiB
C#
499 lines
16 KiB
C#
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)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|