first commit
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user