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