using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using System;
///
/// Central manager for quest system - handles active quests, progress tracking, and rewards
///
public class QuestManager : MonoBehaviour
{
public static QuestManager Instance { get; private set; }
[Header("Quest Configuration")]
[Tooltip("Maximum number of active quests")]
public int maxActiveQuests = 5;
[Tooltip("Game time speed multiplier (how fast time passes)")]
public float gameTimeSpeed = 1f;
[Header("Active Quests")]
[SerializeField] private List activeQuests = new List();
[SerializeField] private List completedQuests = new List();
[SerializeField] private List failedQuests = new List();
[Header("Renown System")]
[SerializeField] private int currentRenown = 0;
[Header("Debug")]
public bool enableDebugLogs = false;
// Events
public event Action OnQuestAccepted;
public event Action> OnQuestCompleted;
public event Action OnQuestFailed;
public event Action OnQuestAbandoned;
public event Action OnQuestGoalCompleted;
public event Action OnRenownChanged;
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
LoadQuestData();
}
else
{
Destroy(gameObject);
}
}
private void Update()
{
UpdateActiveQuests();
}
private void UpdateActiveQuests()
{
if (activeQuests.Count == 0) return;
float deltaTimeHours = Time.deltaTime * gameTimeSpeed;
// Update each active quest
for (int i = activeQuests.Count - 1; i >= 0; i--)
{
var quest = activeQuests[i];
quest.UpdateQuest(deltaTimeHours);
// Remove completed or failed quests from active list
if (quest.status != QuestStatus.Active)
{
activeQuests.RemoveAt(i);
}
}
}
///
/// Accept a new quest
///
public bool AcceptQuest(Quest questData)
{
if (questData == null)
{
LogDebug("Cannot accept null quest");
return false;
}
if (activeQuests.Count >= maxActiveQuests)
{
LogDebug($"Cannot accept quest: Maximum active quests reached ({maxActiveQuests})");
return false;
}
// Check if already have this quest active
if (activeQuests.Any(q => q.questData == questData))
{
LogDebug($"Quest '{questData.questTitle}' is already active");
return false;
}
// Check requirements
if (!MeetsRequirements(questData))
{
LogDebug($"Does not meet requirements for quest '{questData.questTitle}'");
return false;
}
var activeQuest = questData.CreateActiveQuest();
activeQuests.Add(activeQuest);
LogDebug($"✅ Accepted quest: {questData.questTitle}");
OnQuestAccepted?.Invoke(activeQuest);
SaveQuestData();
return true;
}
///
/// Abandon an active quest
///
public bool AbandonQuest(string questId)
{
var quest = activeQuests.FirstOrDefault(q => q.questId == questId);
if (quest == null) return false;
quest.status = QuestStatus.Abandoned;
activeQuests.Remove(quest);
LogDebug($"🚫 Abandoned quest: {quest.questData.questTitle}");
OnQuestAbandoned?.Invoke(quest);
SaveQuestData();
return true;
}
///
/// Progress quest goals - called by game events
///
public void ProgressQuest(QuestGoalType goalType, string targetName = "", int amount = 1)
{
foreach (var quest in activeQuests)
{
if (quest.ProgressGoal(goalType, targetName, amount))
{
// Check if any goals were just completed
foreach (var goal in quest.activeGoals)
{
if (goal.IsCompleted && goal.currentProgress == goal.targetCount)
{
OnQuestGoalCompleted?.Invoke(quest, goal);
}
}
}
}
SaveQuestData();
}
///
/// Try to complete quests at current position
///
public List TryCompleteQuestsAtPosition(Vector2Int position)
{
var completedQuests = new List();
foreach (var quest in activeQuests.ToList())
{
if (quest.TryCompleteQuest(position))
{
completedQuests.Add(quest);
}
}
return completedQuests;
}
///
/// Called when a quest is completed
///
public void CompleteQuest(ActiveQuest quest)
{
if (quest == null) return;
// Move to completed list
completedQuests.Add(quest);
// Generate rewards list
var rewards = GenerateQuestRewards(quest);
// Award rewards
AwardQuestRewards(quest);
// Trigger event with rewards
OnQuestCompleted?.Invoke(quest, rewards);
SaveQuestData();
}
///
/// Called when a quest fails
///
public void FailQuest(ActiveQuest quest)
{
if (quest == null) return;
// Move to failed list
failedQuests.Add(quest);
// Apply penalties
ApplyQuestPenalties(quest);
// Trigger event
OnQuestFailed?.Invoke(quest);
SaveQuestData();
}
private List GenerateQuestRewards(ActiveQuest quest)
{
var rewards = new List();
var questData = quest.questData;
// Add gold reward
if (questData.goldReward > 0)
{
rewards.Add(new QuestReward
{
type = QuestRewardType.Gold,
amount = questData.goldReward
});
}
// Add silver reward
if (questData.silverReward > 0)
{
rewards.Add(new QuestReward
{
type = QuestRewardType.Silver,
amount = questData.silverReward
});
}
// Add copper reward
if (questData.copperReward > 0)
{
rewards.Add(new QuestReward
{
type = QuestRewardType.Copper,
amount = questData.copperReward
});
}
// Add renown reward
if (questData.renownReward > 0)
{
rewards.Add(new QuestReward
{
type = QuestRewardType.Renown,
amount = questData.renownReward
});
}
// Add item rewards
foreach (var itemName in questData.itemRewards)
{
if (!string.IsNullOrEmpty(itemName))
{
rewards.Add(new QuestReward
{
type = QuestRewardType.Item,
amount = 1,
itemName = itemName
});
}
}
return rewards;
}
private void AwardQuestRewards(ActiveQuest quest)
{
var questData = quest.questData;
// Award money to team
var gameStateManager = GameStateManager.Instance;
if (gameStateManager?.savedTeam != null)
{
foreach (var character in gameStateManager.savedTeam)
{
if (character != null)
{
character.gold += questData.goldReward;
character.silver += questData.silverReward;
character.copper += questData.copperReward;
}
}
}
// Award renown
ChangeRenown(questData.renownReward);
// Award items (TODO: implement item reward system)
foreach (var itemName in questData.itemRewards)
{
LogDebug($"🎁 Reward item: {itemName} (TODO: implement item awards)");
}
LogDebug($"💰 Quest rewards: {questData.goldReward}g, {questData.silverReward}s, {questData.copperReward}c, {questData.renownReward} renown");
}
private void ApplyQuestPenalties(ActiveQuest quest)
{
var questData = quest.questData;
// Apply renown penalty
ChangeRenown(-questData.renownPenalty);
LogDebug($"💔 Quest penalties: -{questData.renownPenalty} renown");
}
private void ChangeRenown(int amount)
{
currentRenown = Mathf.Max(0, currentRenown + amount);
OnRenownChanged?.Invoke(currentRenown);
// Save to GameStateManager
if (GameStateManager.Instance != null)
{
// TODO: Add renown field to GameStateManager
}
}
private bool MeetsRequirements(Quest questData)
{
// Check renown requirement
if (currentRenown < questData.minimumRenown)
return false;
// Check prerequisite quests
foreach (var prereq in questData.prerequisiteQuests)
{
if (!completedQuests.Any(q => q.questData.questTitle == prereq))
return false;
}
return true;
}
///
/// Get all active quests
///
public List GetActiveQuests() => new List(activeQuests);
///
/// Get quest by ID
///
public ActiveQuest GetQuestById(string questId) => activeQuests.FirstOrDefault(q => q.questId == questId);
///
/// Get current renown level
///
public int GetRenown() => currentRenown;
///
/// Check if can accept more quests
///
public bool CanAcceptMoreQuests() => activeQuests.Count < maxActiveQuests;
///
/// Clear all quest data - useful for new games
///
public void ClearAllQuests()
{
LogDebug("Clearing all quest data for new game");
activeQuests.Clear();
completedQuests.Clear();
failedQuests.Clear();
currentRenown = 0;
// Clear saved data
PlayerPrefs.DeleteKey("QuestManager_SaveData");
PlayerPrefs.Save();
// Trigger events for UI updates
OnRenownChanged?.Invoke(currentRenown);
LogDebug("All quest data cleared successfully");
}
private void SaveQuestData()
{
try
{
var saveData = new QuestSaveData
{
currentRenown = this.currentRenown,
activeQuests = this.activeQuests.Select(q => q.ToSaveData()).ToList(),
completedQuests = this.completedQuests.Select(q => q.ToSaveData()).ToList(),
failedQuests = this.failedQuests.Select(q => q.ToSaveData()).ToList()
};
string json = JsonUtility.ToJson(saveData, true);
PlayerPrefs.SetString("QuestManager_SaveData", json);
PlayerPrefs.Save();
LogDebug("Quest data saved successfully");
}
catch (System.Exception ex)
{
Debug.LogError($"Failed to save quest data: {ex.Message}");
}
}
public void LoadQuestData()
{
try
{
// Check if this is a new game and clear quest data if so
if (GameStateManager.Instance != null && GameStateManager.Instance.isNewGame)
{
LogDebug("New game detected - clearing all quest data");
ClearAllQuests();
return;
}
string json = PlayerPrefs.GetString("QuestManager_SaveData", "");
if (string.IsNullOrEmpty(json))
{
LogDebug("No quest save data found, starting fresh");
currentRenown = 0;
return;
}
var saveData = JsonUtility.FromJson(json);
if (saveData != null)
{
currentRenown = saveData.currentRenown;
// Load active quests
activeQuests.Clear();
foreach (var questSave in saveData.activeQuests)
{
var activeQuest = ActiveQuest.FromSaveData(questSave);
if (activeQuest != null)
{
activeQuests.Add(activeQuest);
}
}
// Load completed quests
completedQuests.Clear();
foreach (var questSave in saveData.completedQuests)
{
var activeQuest = ActiveQuest.FromSaveData(questSave);
if (activeQuest != null)
{
completedQuests.Add(activeQuest);
}
}
// Load failed quests
failedQuests.Clear();
foreach (var questSave in saveData.failedQuests)
{
var activeQuest = ActiveQuest.FromSaveData(questSave);
if (activeQuest != null)
{
failedQuests.Add(activeQuest);
}
}
LogDebug($"Loaded quest data: {activeQuests.Count} active, {completedQuests.Count} completed, {failedQuests.Count} failed, {currentRenown} renown");
// Trigger renown update event
OnRenownChanged?.Invoke(currentRenown);
// Refresh quest markers on map
StartCoroutine(RefreshMarkersAfterLoad());
}
}
catch (System.Exception ex)
{
Debug.LogError($"Failed to load quest data: {ex.Message}");
currentRenown = 0;
}
}
private System.Collections.IEnumerator RefreshMarkersAfterLoad()
{
// Wait a frame to ensure all systems are initialized
yield return null;
var markerManager = FindFirstObjectByType();
if (markerManager != null)
{
markerManager.RefreshAllMarkers();
}
}
private void LogDebug(string message)
{
if (enableDebugLogs)
{
Debug.Log($"[QuestManager] {message}");
}
}
#region Context Menu Debug Methods
[ContextMenu("Add Test Combat Quest")]
private void AddTestCombatQuest()
{
var testQuest = CreateTestCombatQuest();
AcceptQuest(testQuest);
}
[ContextMenu("Progress Test Quest")]
private void ProgressTestQuest()
{
ProgressQuest(QuestGoalType.KillEnemies, "Bandit", 1);
}
[ContextMenu("Complete Quest at (50,50)")]
private void CompleteQuestAtTestPosition()
{
TryCompleteQuestsAtPosition(new Vector2Int(50, 50));
}
private Quest CreateTestCombatQuest()
{
var quest = ScriptableObject.CreateInstance();
quest.questTitle = "Clear the Bandit Camp";
quest.questDescription = "Bandits have been attacking merchants on the road. Clear out their camp and make the roads safe again.";
quest.questType = QuestType.Combat;
quest.difficulty = QuestDifficulty.Normal;
quest.timeLimitDays = 3;
quest.targetAreaName = "Bandit Camp";
quest.targetMapPosition = new Vector2Int(50, 50);
quest.goldReward = 100;
quest.renownReward = 15;
quest.renownPenalty = 10;
quest.goals.Add(new QuestGoal
{
description = "Defeat 5 Bandits",
goalType = QuestGoalType.KillEnemies,
targetName = "Bandit",
targetCount = 5
});
return quest;
}
#endregion
}
///
/// Save data structure for quest system
///
[System.Serializable]
public class QuestSaveData
{
public int currentRenown;
public List activeQuests = new List();
public List completedQuests = new List();
public List failedQuests = new List();
}
///
/// Save data structure for individual active quests
///
[System.Serializable]
public class ActiveQuestSaveData
{
public string questId;
public string questAssetName; // To find the original Quest asset
public string questTitle; // Fallback identification
public QuestStatus status;
public float elapsedTimeHours;
public List goals = new List();
}
///
/// Save data structure for quest goals
///
[System.Serializable]
public class QuestGoalSaveData
{
public string description;
public QuestGoalType goalType;
public string targetName;
public int targetCount;
public int currentProgress;
public bool isHidden;
public bool isOptional;
}