Files
Click-PointRPG/Assets/Scripts/QuestManager.cs

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}";
}
}
}