| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- using UnityEngine;
- using System.Collections.Generic;
- using System;
- /// <summary>
- /// Runtime instance of a quest that tracks progress and time
- /// </summary>
- [System.Serializable]
- public class ActiveQuest
- {
- [Header("Quest Reference")]
- public Quest questData;
- public string questId; // Unique identifier
- [Header("Progress Tracking")]
- public List<QuestGoal> 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<QuestGoal>();
- 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;
- }
- /// <summary>
- /// Update quest progress (called every frame or game tick)
- /// </summary>
- 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();
- }
- }
- /// <summary>
- /// Progress a specific goal
- /// </summary>
- 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;
- }
- /// <summary>
- /// Check if quest can be completed at current position
- /// </summary>
- public bool CanCompleteAtPosition(Vector2Int position)
- {
- return questData.CanCompleteAtPosition(position) && AreAllRequiredGoalsCompleted();
- }
- /// <summary>
- /// Attempt to complete the quest at given position
- /// </summary>
- 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);
- }
- /// <summary>
- /// Get time remaining as formatted string
- /// </summary>
- 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";
- }
- /// <summary>
- /// Get progress percentage (0-1)
- /// </summary>
- 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;
- }
- /// <summary>
- /// Get urgency level based on time remaining
- /// </summary>
- 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;
- }
- /// <summary>
- /// Get time remaining in hours (used by UI)
- /// </summary>
- public float GetTimeRemaining()
- {
- return timeRemaining;
- }
- /// <summary>
- /// Get urgency level (compatibility method)
- /// </summary>
- public QuestUrgency GetUrgencyLevel()
- {
- return GetUrgency();
- }
- /// <summary>
- /// Get number of completed objectives
- /// </summary>
- public int GetCompletedObjectivesCount()
- {
- int completed = 0;
- foreach (var goal in activeGoals)
- {
- if (goal.currentProgress >= goal.targetCount)
- completed++;
- }
- return completed;
- }
- /// <summary>
- /// Get progress of a specific objective
- /// </summary>
- public int GetObjectiveProgress(int objectiveIndex)
- {
- if (objectiveIndex >= 0 && objectiveIndex < activeGoals.Count)
- return activeGoals[objectiveIndex].currentProgress;
- return 0;
- }
- /// <summary>
- /// Convert this active quest to save data
- /// </summary>
- 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;
- }
- /// <summary>
- /// Create an ActiveQuest from save data
- /// </summary>
- public static ActiveQuest FromSaveData(ActiveQuestSaveData saveData)
- {
- // Try to find the original quest asset
- Quest questAsset = null;
- if (!string.IsNullOrEmpty(saveData.questAssetName))
- {
- questAsset = Resources.Load<Quest>($"Quests/{saveData.questAssetName}");
- if (questAsset == null)
- {
- questAsset = Resources.Load<Quest>(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<Quest>();
- 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
- }
|