| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- using UnityEngine;
- using System.Collections.Generic;
- using System.Linq;
- using System;
- /// <summary>
- /// Central manager for quest system - handles active quests, progress tracking, and rewards
- /// </summary>
- 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<ActiveQuest> activeQuests = new List<ActiveQuest>();
- [SerializeField] private List<ActiveQuest> completedQuests = new List<ActiveQuest>();
- [SerializeField] private List<ActiveQuest> failedQuests = new List<ActiveQuest>();
- [Header("Renown System")]
- [SerializeField] private int currentRenown = 0;
- [Header("Debug")]
- public bool enableDebugLogs = false;
- // Events
- public event Action<ActiveQuest> OnQuestAccepted;
- public event Action<ActiveQuest, List<QuestReward>> OnQuestCompleted;
- public event Action<ActiveQuest> OnQuestFailed;
- public event Action<ActiveQuest> OnQuestAbandoned;
- public event Action<ActiveQuest, QuestGoal> OnQuestGoalCompleted;
- public event Action<int> 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);
- }
- }
- }
- /// <summary>
- /// Accept a new quest
- /// </summary>
- 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;
- }
- /// <summary>
- /// Abandon an active quest
- /// </summary>
- 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;
- }
- /// <summary>
- /// Progress quest goals - called by game events
- /// </summary>
- 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();
- }
- /// <summary>
- /// Try to complete quests at current position
- /// </summary>
- public List<ActiveQuest> TryCompleteQuestsAtPosition(Vector2Int position)
- {
- var completedQuests = new List<ActiveQuest>();
- foreach (var quest in activeQuests.ToList())
- {
- if (quest.TryCompleteQuest(position))
- {
- completedQuests.Add(quest);
- }
- }
- return completedQuests;
- }
- /// <summary>
- /// Called when a quest is completed
- /// </summary>
- 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();
- }
- /// <summary>
- /// Called when a quest fails
- /// </summary>
- 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<QuestReward> GenerateQuestRewards(ActiveQuest quest)
- {
- var rewards = new List<QuestReward>();
- 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;
- }
- /// <summary>
- /// Get all active quests
- /// </summary>
- public List<ActiveQuest> GetActiveQuests() => new List<ActiveQuest>(activeQuests);
- /// <summary>
- /// Get quest by ID
- /// </summary>
- public ActiveQuest GetQuestById(string questId) => activeQuests.FirstOrDefault(q => q.questId == questId);
- /// <summary>
- /// Get current renown level
- /// </summary>
- public int GetRenown() => currentRenown;
- /// <summary>
- /// Check if can accept more quests
- /// </summary>
- public bool CanAcceptMoreQuests() => activeQuests.Count < maxActiveQuests;
- /// <summary>
- /// Clear all quest data - useful for new games
- /// </summary>
- 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<QuestSaveData>(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<QuestMapMarkerManager>();
- 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>();
- 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
- }
- /// <summary>
- /// Save data structure for quest system
- /// </summary>
- [System.Serializable]
- public class QuestSaveData
- {
- public int currentRenown;
- public List<ActiveQuestSaveData> activeQuests = new List<ActiveQuestSaveData>();
- public List<ActiveQuestSaveData> completedQuests = new List<ActiveQuestSaveData>();
- public List<ActiveQuestSaveData> failedQuests = new List<ActiveQuestSaveData>();
- }
- /// <summary>
- /// Save data structure for individual active quests
- /// </summary>
- [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<QuestGoalSaveData> goals = new List<QuestGoalSaveData>();
- }
- /// <summary>
- /// Save data structure for quest goals
- /// </summary>
- [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;
- }
|