using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Adventurer.Runtime; using Core.Runtime; using Quest.Runtime; using UnityEngine; using Random = UnityEngine.Random; namespace Quests.Runtime { public class QuestManager : BaseMonobehaviour { #region Singleton public static QuestManager Instance { get; set; } void OnDestroy() { if (Instance == this) { Instance = null; } } #endregion #region Events public static event Action OnQuestCompleted; public static event Action OnEventReceived; public static event Action OnEventFromQuest; public static event Action> OnAvailableQuestsUpdated; #endregion #region Unity API void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; if (_activeEvents == null) _activeEvents = new List(); } void OnEnable() { GameManager.OnTimeAdvanced += CheckMissionsProgress; } void OnDisable() { GameManager.OnTimeAdvanced -= CheckMissionsProgress; } void Start() { if (_questDatabase != null) { _disponibleQuests = _questDatabase .GetAll() .SelectMany(f => f.questTemplates) .Select(t => t.ToQuestClass(QuestStateEnum.Disponible)) .ToList(); } else { _disponibleQuests = new List(); } // Ensure quest lists are initialized to avoid null issues in consumers if (_activeQuests == null) _activeQuests = new List(); if (_completedQuests == null) _completedQuests = new List(); if (_disponibleQuests == null) _disponibleQuests = new List(); } #endregion #region Properties List _activeQuests; List _disponibleQuests; List _completedQuests; QuestClass _currentQuest; public QuestClass CurrentQuest { get => _currentQuest; set => _currentQuest = value; } public List DisponibleQuests { get => _disponibleQuests; set => _disponibleQuests = value; } public List ActiveQuests { get => _activeQuests; set => _activeQuests = value; } public List CompletedQuests { get => _completedQuests; set => _completedQuests = value; } public List AssignedAdventurers => _currentQuest?.AssignedAdventurersID; [SerializeField] QuestFactoryDatabase _questDatabase; public QuestFactoryDatabase QuestDatabase { get => _questDatabase; set => _questDatabase = value; } public int currentTimeInQuest => _snapTime; int _snapTime = 0; List _activeEvents; // Champ inutilisé - conservé pour référence future List inActiveEvents; #endregion #region Initialization public QuestManager() { // Unity will call constructor before Awake; avoid subscribing here. } #endregion #region Public Methods /// /// Démarre une quête avec une équipe d'aventuriers /// public void StartQuest(QuestClass quest, List team, GameTime gameTime) { // Enforce state order: must be Accepted before starting if (quest.State != QuestStateEnum.Accepted) { Debug.LogWarning($"Cannot start quest '{quest.Name}' because it is not in Accepted state (current: {quest.State})."); return; } AssignAdventurersToQuest(quest, team); SetQuestTimings(quest, gameTime); UpdateQuestStatus(quest); } /// /// Complète une quête et libère les aventuriers assignés /// public void CompleteQuest(QuestClass quest, List team) { if (quest.State != QuestStateEnum.InProgress) return; ReleaseAdventurers(team); UpdateQuestCompletionStatus(quest); SaveQuestProgress(); } /// /// Notifie les observateurs des quêtes complétées /// public void NotifyCompletedQuests() { foreach (var quest in _completedQuests) { OnQuestCompleted?.Invoke(quest); } } /// /// Récupère l'historique des events lié à une quête /// public QuestSummary GetQuestHistory(Guid questId) { QuestClass quest = GetQuestById(questId); var dict = GetFact>>("events_quests_history"); List events = null; if (dict != null && dict.TryGetValue(questId, out var history)) { events = history; } else { events = new List(); } return new QuestSummary(quest, events); } /// /// Vérifie si des aventuriers peuvent être sélectionnés pour la quête courante /// public bool CanSelectedAdventurers() { // Adventurers can be selected only when the quest has been accepted return _currentQuest != null && _currentQuest.State == QuestStateEnum.Accepted; } /// /// Vérifie si une quête est complétée par son nom /// public bool IsQuestCompleted(string questName) { return CompletedQuests != null && CompletedQuests.Any(q => q.Name == questName); } /// /// Résout une liste de quêtes à partir des données sauvegardées /// public List ResolveQuestsList(List questsFromSave) { List quests = new List(); foreach (var saved in questsFromSave) { QuestTemplate template = _questDatabase.GetTemplatesByName(saved.Name); if (template == null) continue; var rebuilt = template.ToQuestClass(saved.State, saved.ID); // Restaurer les infos runtime utiles rebuilt.StartSeconds = saved.StartSeconds; rebuilt.EndSeconds = saved.EndSeconds; if (saved.AssignedAdventurersID != null) rebuilt.AssignedAdventurersID = new List(saved.AssignedAdventurersID); quests.Add(rebuilt); } return quests; } public List GetAvailableQuests(int level) { var factory = _questDatabase.GetFactoryForLevel(level); if (factory == null) return new List(); return factory.questTemplates .Where(q => q.data.MinLevel <= level) .ToList(); } public void NotifyAvailableQuestsUpdated(int level) { var available = GetAvailableQuests(level); OnAvailableQuestsUpdated?.Invoke(available); } #endregion #region Utils /// /// Assigne des aventuriers à une quête /// void AssignAdventurersToQuest(QuestClass quest, List team) { foreach (var adventurer in team) { adventurer.IsAvailable = false; if (quest.AssignedAdventurersID == null) quest.AssignedAdventurersID = new List(); quest.AssignedAdventurersID.Add(adventurer.ID); } // Synchroniser la sauvegarde de la quête avec les aventuriers assignés var saveQuests = GetFact>("accepted_quests"); if (saveQuests != null) { var saveQuest = saveQuests.FirstOrDefault(q => q.ID == quest.ID); if (saveQuest != null) { saveQuest.AssignedAdventurersID = new List(quest.AssignedAdventurersID); } SaveFacts(); } } /// /// Configure les temps de début et de fin d'une quête /// void SetQuestTimings(QuestClass quest, GameTime gameTime) { quest.State = QuestStateEnum.InProgress; quest.StartSeconds = gameTime.TotalSeconds; quest.EndSeconds = gameTime.TotalSeconds + (quest.Duration * 60); } /// /// Met à jour le statut d'une quête dans les listes actives et sauvegardées /// void UpdateQuestStatus(QuestClass quest) { // Avoid duplicates in active quests if (_activeQuests.All(q => q.ID != quest.ID)) { _activeQuests.Add(quest); } List saveQuests = GetFact>(("accepted_quests")); foreach (var saveQuest in saveQuests) { if (saveQuest.Name == quest.Name) { saveQuest.State = QuestStateEnum.InProgress; saveQuest.StartSeconds = quest.StartSeconds; saveQuest.EndSeconds = quest.EndSeconds; } } SaveFacts(); } /// /// Libère les aventuriers assignés à une quête /// void ReleaseAdventurers(List team) { bool anyChanged = false; foreach (var adventurerId in team) { AdventurerClass adventurer = QuestClass.GetOneAdventurerFromId(adventurerId); if (adventurer == null) { Info($"Aventurer {adventurerId} introuvable"); continue; } Info($"{adventurer.Name} est dans la team avec le status dispo : {adventurer.IsAvailable}"); if (adventurer != null && adventurer.IsAvailable == false) { adventurer.IsAvailable = true; anyChanged = true; } } if (anyChanged) { Info("Comme les données on changées, on les sauvegarde."); SaveFacts(); } } /// /// Met à jour le statut de complétion d'une quête /// void UpdateQuestCompletionStatus(QuestClass quest) { quest.State = QuestStateEnum.Completed; _activeQuests.RemoveAll(q => q.Name == quest.Name); _completedQuests.Add(quest); List quests = GetFact>(("accepted_quests")); QuestClass questToUpdate = quests.FirstOrDefault(q => q.ID == quest.ID); if (questToUpdate != null) { questToUpdate.State = QuestStateEnum.Completed; } NotifyCompletedQuests(); } /// /// Sauvegarde les progrès des quêtes /// void SaveQuestProgress() { SaveFacts(); } /// /// Vérifie la progression des missions actives /// void CheckMissionsProgress(int currentSeconds) { if(_activeQuests == null) return; var questsToComplete = new List(); var activeQuests = _activeQuests.Where(q => q.State == QuestStateEnum.InProgress).ToList(); foreach (var quest in activeQuests) { _snapTime = currentSeconds - quest.StartSeconds; CheckQuestEvents(quest, currentSeconds); if (quest.State == QuestStateEnum.InProgress && currentSeconds >= quest.EndSeconds) { questsToComplete.Add(quest); } } foreach (var quest in questsToComplete) { CompleteQuest(quest, quest.AssignedAdventurersID); } } /// /// Vérifie et déclenche les événements de quête lorsque les conditions sont remplies /// void CheckQuestEvents(QuestClass quest, int currentSeconds) { if (quest?.ActiveEvents == null || quest.TriggeredEventsDescriptionKeys == null) return; foreach (var questEvent in quest.ActiveEvents) { if (questEvent == null) continue; if (quest.TriggeredEventsDescriptionKeys.Contains(questEvent.DescriptionKey)) continue; // Skip malformed time windows if (questEvent.MinTimeTrigger > questEvent.MaxTimeTrigger) continue; if (_snapTime >= questEvent.MinTimeTrigger && _snapTime <= questEvent.MaxTimeTrigger) { float percent = Mathf.Clamp(questEvent.PercentTrigger, 0f, 100f); if (Random.Range(0f, 100f) <= percent) { TriggerEvent(questEvent, quest); quest.TriggeredEventsDescriptionKeys.Add(questEvent.DescriptionKey); } } } } /// /// Déclenche un événement de quête /// void TriggerEvent(QuestEvent questEvent, QuestClass quest) { questEvent.Time = _snapTime; OnEventReceived?.Invoke(questEvent); OnEventFromQuest?.Invoke(quest); var targets = questEvent.GetTargets(quest.AssignedAdventurersID); ApplyEffect(questEvent.Effects, targets); var eventsDict = GetFact>>("events_quests_history"); if (eventsDict == null) { eventsDict = new Dictionary>(); SetFact("events_quests_history", eventsDict, FactPersistence.Persistent); } if(!eventsDict.ContainsKey(quest.ID)) { eventsDict.Add(quest.ID, new List()); } QuestEventLog questEventLog = new QuestEventLog(_snapTime, questEvent.Id); eventsDict[quest.ID].Add(questEventLog); if (_activeEvents == null) _activeEvents = new List(); _activeEvents.Add(questEvent); SaveFacts(); } /// /// Applique les effets aux aventuriers ciblés /// void ApplyEffect(List effects, List targets) { foreach (var effect in effects) { foreach (var target in targets) { ApplyEffectToTarget(effect, target); } } } /// /// Applique un effet spécifique à un aventurier cible /// void ApplyEffectToTarget(EventEffect effect, AdventurerClass target) { switch (effect.Type) { case EffectType.Damage: target.TakeDamage(effect.Value); break; case EffectType.Heal: target.Heal(effect.Value); break; case EffectType.Buff: target.ApplyBuff(effect.Value); break; } } /// /// Récupère une quête via son ID /// /// /// QuestClass GetQuestById(Guid questId) { QuestClass quest = _activeQuests.FirstOrDefault(q => q.ID == questId); if(quest != null) return quest; quest = _completedQuests.FirstOrDefault(q => q.ID == questId); if(quest != null) return quest; foreach (var factory in _questDatabase.GetAll()) { foreach (var template in factory.questTemplates) { if (Guid.TryParse(template.m_assetGuid, out Guid guid) && guid == questId) { return template.ToQuestClass(QuestStateEnum.Disponible, questId); } } } return null; } /// /// Récupère un event via son ID /// /// /// QuestEvent public QuestEvent GetEventById(Guid eventId) { if (_activeEvents == null) return null; return _activeEvents.FirstOrDefault(e => e.Id == eventId); } #endregion } }