using UnityEngine; using System.Collections.Generic; using System; /// /// Runtime instance of a quest that tracks progress and time /// [System.Serializable] public class ActiveQuest { [Header("Quest Reference")] public Quest questData; public string questId; // Unique identifier [Header("Progress Tracking")] public List activeGoals; public QuestStatus status = QuestStatus.Active; [Header("Time Tracking")] public float timeStarted; // Game time when quest was accepted public float timeRemaining; // Hours remaining [Header("Completion Data")] public float timeCompleted = -1f; public Vector2Int completionPosition; public ActiveQuest(Quest quest) { questData = quest; questId = System.Guid.NewGuid().ToString(); // Copy goals from quest data activeGoals = new List(); foreach (var goal in quest.goals) { activeGoals.Add(new QuestGoal { description = goal.description, goalType = goal.goalType, targetName = goal.targetName, targetCount = goal.targetCount, currentProgress = 0, isHidden = goal.isHidden, isOptional = goal.isOptional }); } timeStarted = Time.time; timeRemaining = quest.GetTotalTimeLimitHours(); status = QuestStatus.Active; } /// /// Update quest progress (called every frame or game tick) /// public void UpdateQuest(float deltaTimeHours) { if (status != QuestStatus.Active) return; timeRemaining -= deltaTimeHours; // Check if time has run out if (timeRemaining <= 0f && questData.canFail) { status = QuestStatus.Failed; OnQuestFailed(); return; } // Check if all required goals are completed if (AreAllRequiredGoalsCompleted()) { status = QuestStatus.Completed; timeCompleted = Time.time; OnQuestCompleted(); } } /// /// Progress a specific goal /// public bool ProgressGoal(QuestGoalType goalType, string targetName = "", int amount = 1) { bool progressMade = false; foreach (var goal in activeGoals) { if (goal.goalType == goalType && (string.IsNullOrEmpty(targetName) || goal.targetName.Equals(targetName, StringComparison.OrdinalIgnoreCase))) { int oldProgress = goal.currentProgress; goal.currentProgress = Mathf.Min(goal.currentProgress + amount, goal.targetCount); if (goal.currentProgress > oldProgress) { progressMade = true; Debug.Log($"🎯 Quest Progress: {goal.description} ({goal.currentProgress}/{goal.targetCount})"); } } } return progressMade; } /// /// Check if quest can be completed at current position /// public bool CanCompleteAtPosition(Vector2Int position) { return questData.CanCompleteAtPosition(position) && AreAllRequiredGoalsCompleted(); } /// /// Attempt to complete the quest at given position /// public bool TryCompleteQuest(Vector2Int position) { if (!CanCompleteAtPosition(position)) return false; status = QuestStatus.Completed; timeCompleted = Time.time; completionPosition = position; OnQuestCompleted(); return true; } private bool AreAllRequiredGoalsCompleted() { foreach (var goal in activeGoals) { if (!goal.isOptional && !goal.IsCompleted) return false; } return true; } private void OnQuestCompleted() { Debug.Log($"✅ Quest Completed: {questData.questTitle}"); QuestManager.Instance?.CompleteQuest(this); } private void OnQuestFailed() { Debug.Log($"❌ Quest Failed: {questData.questTitle} (Time's up!)"); QuestManager.Instance?.FailQuest(this); } /// /// Get time remaining as formatted string /// public string GetTimeRemainingString() { if (timeRemaining <= 0) return "EXPIRED"; int days = Mathf.FloorToInt(timeRemaining / 24f); int hours = Mathf.FloorToInt(timeRemaining % 24f); int minutes = Mathf.FloorToInt((timeRemaining % 1f) * 60f); if (days > 0) return $"{days}d {hours}h {minutes}m"; else if (hours > 0) return $"{hours}h {minutes}m"; else return $"{minutes}m"; } /// /// Get progress percentage (0-1) /// public float GetOverallProgress() { if (activeGoals.Count == 0) return 1f; float totalProgress = 0f; int requiredGoals = 0; foreach (var goal in activeGoals) { if (!goal.isOptional) { totalProgress += goal.ProgressPercent; requiredGoals++; } } return requiredGoals > 0 ? totalProgress / requiredGoals : 1f; } /// /// Get urgency level based on time remaining /// public QuestUrgency GetUrgency() { float totalTime = questData.GetTotalTimeLimitHours(); float timePercent = timeRemaining / totalTime; if (timePercent > 0.75f) return QuestUrgency.Low; if (timePercent > 0.5f) return QuestUrgency.Medium; if (timePercent > 0.25f) return QuestUrgency.High; return QuestUrgency.Critical; } /// /// Get time remaining in hours (used by UI) /// public float GetTimeRemaining() { return timeRemaining; } /// /// Get urgency level (compatibility method) /// public QuestUrgency GetUrgencyLevel() { return GetUrgency(); } /// /// Get number of completed objectives /// public int GetCompletedObjectivesCount() { int completed = 0; foreach (var goal in activeGoals) { if (goal.currentProgress >= goal.targetCount) completed++; } return completed; } /// /// Get progress of a specific objective /// public int GetObjectiveProgress(int objectiveIndex) { if (objectiveIndex >= 0 && objectiveIndex < activeGoals.Count) return activeGoals[objectiveIndex].currentProgress; return 0; } /// /// Convert this active quest to save data /// public ActiveQuestSaveData ToSaveData() { var saveData = new ActiveQuestSaveData { questId = this.questId, questAssetName = questData?.name ?? "", questTitle = questData?.questTitle ?? "Unknown Quest", status = this.status, elapsedTimeHours = (questData?.GetTotalTimeLimitHours() ?? 0) - timeRemaining }; // Save goal progress foreach (var goal in activeGoals) { saveData.goals.Add(new QuestGoalSaveData { description = goal.description, goalType = goal.goalType, targetName = goal.targetName, targetCount = goal.targetCount, currentProgress = goal.currentProgress, isHidden = goal.isHidden, isOptional = goal.isOptional }); } return saveData; } /// /// Create an ActiveQuest from save data /// public static ActiveQuest FromSaveData(ActiveQuestSaveData saveData) { // Try to find the original quest asset Quest questAsset = null; if (!string.IsNullOrEmpty(saveData.questAssetName)) { questAsset = Resources.Load($"Quests/{saveData.questAssetName}"); if (questAsset == null) { questAsset = Resources.Load(saveData.questAssetName); } } // If we can't find the asset, create a minimal one if (questAsset == null) { Debug.LogWarning($"Could not find quest asset '{saveData.questAssetName}' for saved quest '{saveData.questTitle}'. Creating placeholder."); questAsset = ScriptableObject.CreateInstance(); questAsset.questTitle = saveData.questTitle; questAsset.questDescription = "This quest was loaded from save data but the original asset is missing."; questAsset.timeLimitDays = 7; // Default } // Create the active quest var activeQuest = new ActiveQuest(questAsset); activeQuest.questId = saveData.questId; activeQuest.status = saveData.status; // Calculate time remaining from elapsed time float totalTime = questAsset.GetTotalTimeLimitHours(); activeQuest.timeRemaining = Mathf.Max(0, totalTime - saveData.elapsedTimeHours); // Restore goal progress for (int i = 0; i < activeQuest.activeGoals.Count && i < saveData.goals.Count; i++) { var goal = activeQuest.activeGoals[i]; var savedGoal = saveData.goals[i]; goal.currentProgress = savedGoal.currentProgress; } return activeQuest; } } public enum QuestStatus { Active, Completed, Failed, Abandoned } public enum QuestUrgency { Low, Medium, High, Critical }