277 lines
7.5 KiB
C#
277 lines
7.5 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|