179 lines
5.3 KiB
C#
179 lines
5.3 KiB
C#
|
|
using System.Collections.Generic;
|
||
|
|
using UnityEngine;
|
||
|
|
using UnityEngine.InputSystem;
|
||
|
|
|
||
|
|
public enum ObjectiveType { Interact, Kill, Collect, None }
|
||
|
|
|
||
|
|
[System.Serializable]
|
||
|
|
public class QuestObjective
|
||
|
|
{
|
||
|
|
[Tooltip("Unique ID for this objective (e.g., 'bandit_leader', 'wolf_pelt')")]
|
||
|
|
public string targetID;
|
||
|
|
|
||
|
|
[Tooltip("What action the player must perform")]
|
||
|
|
public ObjectiveType type;
|
||
|
|
|
||
|
|
[Tooltip("The text shown in the UI")]
|
||
|
|
public string description;
|
||
|
|
|
||
|
|
[Header("Counting")]
|
||
|
|
public bool requiresCount;
|
||
|
|
public int currentCount;
|
||
|
|
public int targetCount;
|
||
|
|
|
||
|
|
[HideInInspector]
|
||
|
|
public bool isCompleted;
|
||
|
|
}
|
||
|
|
|
||
|
|
[System.Serializable]
|
||
|
|
public class Quest
|
||
|
|
{
|
||
|
|
public string questName;
|
||
|
|
public List<QuestObjective> objectives = new List<QuestObjective>();
|
||
|
|
public bool isCompleted;
|
||
|
|
}
|
||
|
|
|
||
|
|
public class QuestManager : MonoBehaviour
|
||
|
|
{
|
||
|
|
public static QuestManager Instance { get; private set; }
|
||
|
|
|
||
|
|
[Header("Current Quest")]
|
||
|
|
public Quest activeQuest;
|
||
|
|
|
||
|
|
[Header("Testing")]
|
||
|
|
[Tooltip("Set the key to automatically progress the first incomplete objective")]
|
||
|
|
public Key completeObjectiveKey = Key.O;
|
||
|
|
|
||
|
|
// We notify the UI when the quest updates
|
||
|
|
public delegate void OnQuestUpdated();
|
||
|
|
public event OnQuestUpdated onQuestUpdated;
|
||
|
|
|
||
|
|
private void Awake()
|
||
|
|
{
|
||
|
|
if (Instance == null)
|
||
|
|
{
|
||
|
|
Instance = this;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
Destroy(gameObject);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void Start()
|
||
|
|
{
|
||
|
|
// Tell UI to draw whatever quest is assigned in the inspector (if any)
|
||
|
|
NotifyUI();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void Update()
|
||
|
|
{
|
||
|
|
if (Keyboard.current != null && Keyboard.current[completeObjectiveKey].wasPressedThisFrame)
|
||
|
|
{
|
||
|
|
// For testing: progress the first incomplete objective
|
||
|
|
if (activeQuest != null && !activeQuest.isCompleted)
|
||
|
|
{
|
||
|
|
foreach (var obj in activeQuest.objectives)
|
||
|
|
{
|
||
|
|
if (!obj.isCompleted)
|
||
|
|
{
|
||
|
|
ProgressObjective(obj.type, obj.targetID, 1);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// NPCs or Items can call this to progress the current quest!
|
||
|
|
/// Example: QuestManager.Instance.ProgressObjective(ObjectiveType.Kill, "wolf");
|
||
|
|
/// </summary>
|
||
|
|
public bool ProgressObjective(ObjectiveType type, string targetID, int amount = 1)
|
||
|
|
{
|
||
|
|
if (activeQuest == null || activeQuest.isCompleted) return false;
|
||
|
|
|
||
|
|
foreach (var obj in activeQuest.objectives)
|
||
|
|
{
|
||
|
|
// Only progress the FIRST incomplete objective (Linear Quests)
|
||
|
|
if (!obj.isCompleted)
|
||
|
|
{
|
||
|
|
// Check if this objective matches the action
|
||
|
|
if (obj.type == type && obj.targetID == targetID)
|
||
|
|
{
|
||
|
|
if (obj.requiresCount)
|
||
|
|
{
|
||
|
|
obj.currentCount += amount;
|
||
|
|
Debug.Log($"[QuestManager] Progressed '{obj.description}': {obj.currentCount}/{obj.targetCount}");
|
||
|
|
|
||
|
|
if (obj.currentCount >= obj.targetCount)
|
||
|
|
{
|
||
|
|
obj.currentCount = obj.targetCount;
|
||
|
|
obj.isCompleted = true;
|
||
|
|
Debug.Log($"[QuestManager] Objective Completed: {obj.description}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
obj.isCompleted = true;
|
||
|
|
Debug.Log($"[QuestManager] Objective Completed: {obj.description}");
|
||
|
|
}
|
||
|
|
|
||
|
|
CheckQuestCompletion();
|
||
|
|
NotifyUI();
|
||
|
|
return true; // Successfully progressed!
|
||
|
|
}
|
||
|
|
|
||
|
|
// If it didn't match, we still stop here because we only allow
|
||
|
|
// progressing the CURRENT active objective.
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Checks if a specific objective is the CURRENT active objective the player needs to do.
|
||
|
|
/// </summary>
|
||
|
|
public bool IsObjectiveActive(ObjectiveType type, string targetID)
|
||
|
|
{
|
||
|
|
if (activeQuest == null || activeQuest.isCompleted) return false;
|
||
|
|
|
||
|
|
foreach (var obj in activeQuest.objectives)
|
||
|
|
{
|
||
|
|
if (!obj.isCompleted)
|
||
|
|
{
|
||
|
|
// We found the FIRST incomplete objective! Is it this one?
|
||
|
|
return (obj.type == type && obj.targetID == targetID);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void CheckQuestCompletion()
|
||
|
|
{
|
||
|
|
foreach (var obj in activeQuest.objectives)
|
||
|
|
{
|
||
|
|
if (!obj.isCompleted) return; // Still have objectives left
|
||
|
|
}
|
||
|
|
|
||
|
|
activeQuest.isCompleted = true;
|
||
|
|
Debug.Log($"[QuestManager] Quest Completed: {activeQuest.questName}");
|
||
|
|
}
|
||
|
|
|
||
|
|
public void NotifyUI()
|
||
|
|
{
|
||
|
|
onQuestUpdated?.Invoke();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// An NPC or Item can call this to give the player a brand new quest!
|
||
|
|
/// </summary>
|
||
|
|
public void StartNewQuest(Quest newQuest)
|
||
|
|
{
|
||
|
|
activeQuest = newQuest;
|
||
|
|
NotifyUI();
|
||
|
|
Debug.Log($"[QuestManager] Started new quest: {newQuest.questName}");
|
||
|
|
}
|
||
|
|
}
|