392 lines
10 KiB
C#
392 lines
10 KiB
C#
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine.Events;
|
|
using TMPro;
|
|
|
|
/// <summary>
|
|
/// Centralized manager for all quests in the game
|
|
/// Handles quest state transitions and tracking
|
|
/// </summary>
|
|
public class QuestManager : MonoBehaviour
|
|
{
|
|
// Singleton instance
|
|
public static QuestManager Instance { get; private set; }
|
|
|
|
[Header("UI")]
|
|
[SerializeField]
|
|
private TextMeshProUGUI currentQuestTitleText;
|
|
[SerializeField]
|
|
private TextMeshProUGUI currentQuestDescriptionText;
|
|
|
|
[Header("Quest Database")]
|
|
[Tooltip("All available quests in the game")]
|
|
public List<Quest> availableQuests = new List<Quest>();
|
|
|
|
[Header("Runtime Quest Tracking")]
|
|
[SerializeField]
|
|
private List<Quest> activeQuests = new List<Quest>();
|
|
[SerializeField]
|
|
private List<Quest> completedQuests = new List<Quest>();
|
|
[SerializeField]
|
|
private List<Quest> failedQuests = new List<Quest>();
|
|
|
|
[Header("Events")]
|
|
public UnityEvent<Quest> onQuestStarted;
|
|
public UnityEvent<Quest> onQuestProgressed;
|
|
public UnityEvent<Quest> onQuestCompleted;
|
|
public UnityEvent<Quest> onQuestFailed;
|
|
|
|
void Awake()
|
|
{
|
|
// Singleton pattern
|
|
if (Instance != null && Instance != this)
|
|
{
|
|
Debug.LogWarning("Multiple QuestManager instances detected. Destroying duplicate.", gameObject);
|
|
Destroy(this);
|
|
return;
|
|
}
|
|
Instance = this;
|
|
}
|
|
|
|
void OnDestroy()
|
|
{
|
|
if (Instance == this)
|
|
Instance = null;
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
UpdateQuestUIFromCurrent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start a quest by ID
|
|
/// </summary>
|
|
public bool StartQuest(string questId)
|
|
{
|
|
Quest quest = GetQuestById(questId);
|
|
if (quest == null)
|
|
{
|
|
Debug.LogWarning($"Quest '{questId}' not found in available quests.");
|
|
return false;
|
|
}
|
|
|
|
return StartQuest(quest);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start a quest
|
|
/// </summary>
|
|
public bool StartQuest(Quest quest)
|
|
{
|
|
if (quest == null)
|
|
{
|
|
Debug.LogWarning("Cannot start null quest.");
|
|
return false;
|
|
}
|
|
|
|
// Check if already active
|
|
if (activeQuests.Contains(quest))
|
|
{
|
|
Debug.Log($"Quest '{quest.questName}' is already active.");
|
|
return false;
|
|
}
|
|
|
|
// Check if can start
|
|
if (!quest.CanStart())
|
|
{
|
|
Debug.Log($"Quest '{quest.questName}' cannot be started (current state: {quest.state}).");
|
|
return false;
|
|
}
|
|
|
|
// Check prerequisites
|
|
if (!string.IsNullOrEmpty(quest.prerequisiteQuestId))
|
|
{
|
|
Quest prerequisite = GetQuestById(quest.prerequisiteQuestId);
|
|
if (prerequisite != null && prerequisite.state != QuestState.Completed)
|
|
{
|
|
Debug.Log($"Quest '{quest.questName}' requires completing '{prerequisite.questName}' first.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Start the quest
|
|
quest.state = QuestState.Active;
|
|
quest.currentProgress = 0;
|
|
activeQuests.Add(quest);
|
|
|
|
Debug.Log($"Quest Started: {quest.questName}");
|
|
onQuestStarted?.Invoke(quest);
|
|
UpdateQuestUI(quest);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Progress a quest by ID
|
|
/// </summary>
|
|
public bool ProgressQuest(string questId, int amount = 1)
|
|
{
|
|
Quest quest = GetActiveQuest(questId);
|
|
if (quest == null)
|
|
{
|
|
Debug.LogWarning($"Active quest '{questId}' not found.");
|
|
return false;
|
|
}
|
|
|
|
return ProgressQuest(quest, amount);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Progress a quest
|
|
/// </summary>
|
|
public bool ProgressQuest(Quest quest, int amount = 1)
|
|
{
|
|
if (quest == null || !quest.IsActive())
|
|
{
|
|
Debug.LogWarning("Cannot progress quest - not active.");
|
|
return false;
|
|
}
|
|
|
|
int previousProgress = quest.currentProgress;
|
|
quest.AddProgress(amount);
|
|
|
|
if (quest.currentProgress > previousProgress)
|
|
{
|
|
Debug.Log($"Quest Progress: {quest.questName} ({quest.currentProgress}/{quest.requiredProgress})");
|
|
onQuestProgressed?.Invoke(quest);
|
|
UpdateQuestUI(quest);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Complete a quest by ID
|
|
/// </summary>
|
|
public bool CompleteQuest(string questId)
|
|
{
|
|
Quest quest = GetActiveQuest(questId);
|
|
if (quest == null)
|
|
{
|
|
Debug.LogWarning($"Active quest '{questId}' not found.");
|
|
return false;
|
|
}
|
|
|
|
return CompleteQuest(quest);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Complete a quest and grant rewards
|
|
/// </summary>
|
|
public bool CompleteQuest(Quest quest)
|
|
{
|
|
if (quest == null)
|
|
{
|
|
Debug.LogWarning("Cannot complete null quest.");
|
|
return false;
|
|
}
|
|
|
|
if (!quest.IsComplete())
|
|
{
|
|
Debug.Log($"Quest '{quest.questName}' objectives not met ({quest.currentProgress}/{quest.requiredProgress}).");
|
|
return false;
|
|
}
|
|
|
|
// Remove from active, add to completed
|
|
activeQuests.Remove(quest);
|
|
quest.state = QuestState.Completed;
|
|
|
|
if (!completedQuests.Contains(quest))
|
|
completedQuests.Add(quest);
|
|
|
|
// Grant rewards
|
|
GrantQuestRewards(quest);
|
|
|
|
Debug.Log($"Quest Completed: {quest.questName}");
|
|
onQuestCompleted?.Invoke(quest);
|
|
UpdateQuestUIFromCurrent();
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fail a quest by ID
|
|
/// </summary>
|
|
public bool FailQuest(string questId)
|
|
{
|
|
Quest quest = GetActiveQuest(questId);
|
|
if (quest == null)
|
|
{
|
|
Debug.LogWarning($"Active quest '{questId}' not found.");
|
|
return false;
|
|
}
|
|
|
|
return FailQuest(quest);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fail a quest
|
|
/// </summary>
|
|
public bool FailQuest(Quest quest)
|
|
{
|
|
if (quest == null)
|
|
{
|
|
Debug.LogWarning("Cannot fail null quest.");
|
|
return false;
|
|
}
|
|
|
|
if (!quest.IsActive())
|
|
{
|
|
Debug.LogWarning($"Quest '{quest.questName}' is not active.");
|
|
return false;
|
|
}
|
|
|
|
// Remove from active, add to failed
|
|
activeQuests.Remove(quest);
|
|
quest.state = QuestState.Failed;
|
|
|
|
if (!failedQuests.Contains(quest))
|
|
failedQuests.Add(quest);
|
|
|
|
Debug.Log($"Quest Failed: {quest.questName}");
|
|
onQuestFailed?.Invoke(quest);
|
|
UpdateQuestUIFromCurrent();
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Grant rewards to player (override/extend this for your reward system)
|
|
/// </summary>
|
|
protected virtual void GrantQuestRewards(Quest quest)
|
|
{
|
|
Debug.Log($"Granting rewards: {quest.experienceReward} XP, {quest.goldReward} Gold");
|
|
|
|
if (quest.itemRewards != null && quest.itemRewards.Length > 0)
|
|
{
|
|
Debug.Log($"Granting items: {string.Join(", ", quest.itemRewards)}");
|
|
}
|
|
|
|
// TODO: Implement actual reward granting to player
|
|
// e.g., Player.current.AddExperience(quest.experienceReward);
|
|
// e.g., Player.current.AddGold(quest.goldReward);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get quest by ID from available quests
|
|
/// </summary>
|
|
public Quest GetQuestById(string questId)
|
|
{
|
|
return availableQuests.FirstOrDefault(q => q.questId == questId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get active quest by ID
|
|
/// </summary>
|
|
public Quest GetActiveQuest(string questId)
|
|
{
|
|
return activeQuests.FirstOrDefault(q => q.questId == questId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if quest is active
|
|
/// </summary>
|
|
public bool IsQuestActive(string questId)
|
|
{
|
|
return activeQuests.Any(q => q.questId == questId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if quest is completed
|
|
/// </summary>
|
|
public bool IsQuestCompleted(string questId)
|
|
{
|
|
return completedQuests.Any(q => q.questId == questId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if player can accept this quest
|
|
/// </summary>
|
|
public bool CanAcceptQuest(string questId)
|
|
{
|
|
Quest quest = GetQuestById(questId);
|
|
if (quest == null || !quest.CanStart()) return false;
|
|
|
|
// Check prerequisites
|
|
if (!string.IsNullOrEmpty(quest.prerequisiteQuestId))
|
|
{
|
|
if (!IsQuestCompleted(quest.prerequisiteQuestId))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all active quests
|
|
/// </summary>
|
|
public List<Quest> GetActiveQuests()
|
|
{
|
|
return new List<Quest>(activeQuests);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all completed quests
|
|
/// </summary>
|
|
public List<Quest> GetCompletedQuests()
|
|
{
|
|
return new List<Quest>(completedQuests);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset all quest progress (for testing or new game)
|
|
/// </summary>
|
|
public void ResetAllQuests()
|
|
{
|
|
foreach (Quest quest in availableQuests)
|
|
{
|
|
quest.Reset();
|
|
}
|
|
|
|
activeQuests.Clear();
|
|
completedQuests.Clear();
|
|
failedQuests.Clear();
|
|
|
|
Debug.Log("All quests reset.");
|
|
UpdateQuestUIFromCurrent();
|
|
}
|
|
|
|
private void UpdateQuestUIFromCurrent()
|
|
{
|
|
Quest current = activeQuests.FirstOrDefault();
|
|
UpdateQuestUI(current);
|
|
}
|
|
|
|
private void UpdateQuestUI(Quest quest)
|
|
{
|
|
if (currentQuestTitleText == null && currentQuestDescriptionText == null)
|
|
return;
|
|
|
|
if (quest == null)
|
|
{
|
|
if (currentQuestTitleText != null)
|
|
currentQuestTitleText.text = "No Active Quest";
|
|
if (currentQuestDescriptionText != null)
|
|
currentQuestDescriptionText.text = string.Empty;
|
|
return;
|
|
}
|
|
|
|
if (currentQuestTitleText != null)
|
|
currentQuestTitleText.text = quest.questName;
|
|
|
|
if (currentQuestDescriptionText != null)
|
|
{
|
|
currentQuestDescriptionText.text =
|
|
$"Objective: {quest.questName}\nProgress: {quest.currentProgress}/{quest.requiredProgress}";
|
|
}
|
|
}
|
|
}
|