using System; 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 static QuestManager _instance; public static QuestManager Instance => _instance ??= new QuestManager(); #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 Properties QuestClass _currentQuest; public QuestClass CurrentQuest { get => _currentQuest; set => _currentQuest = value; } List _activeQuests; public List ActiveQuests { get => _activeQuests; set => _activeQuests = value; } List _completedQuests; 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() { _activeEvents = new List(); GameManager.OnTimeAdvanced += CheckMissionsProgress; } #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); List events = GetFact>>("events_quests_history")[questId]; 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 ActiveQuests != null && ActiveQuests.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 quest in questsFromSave) { QuestTemplate template = _questDatabase.GetTemplatesByName(quest.Name); if (template == null) continue; quests.Add(template.ToQuestClass(quest.State, quest.ID)); } 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 Methods /// /// 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); } } /// /// 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) { foreach (var adventurerId in team) { AdventurerClass adventurer = QuestClass.GetOneAdventurerFromId(adventurerId); if (adventurer != null) adventurer.IsAvailable = true; } } /// /// 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) { foreach (var questEvent in quest.ActiveEvents) { if (quest.TriggeredEventsDescriptionKeys.Contains(questEvent.DescriptionKey)) continue; if (_snapTime >= questEvent.MinTimeTrigger && _snapTime <= questEvent.MaxTimeTrigger) { if (Random.Range(0f, 100f) <= questEvent.PercentTrigger) { 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); Dictionary> events = GetFact>>("events_quests_history"); if(!events.ContainsKey(quest.ID)) { events.Add(quest.ID, new List()); } QuestEventLog questEventLog = new QuestEventLog(_snapTime, questEvent.Id); events[quest.ID].Add(questEventLog); _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 } }