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
}