using System.Collections; using System.Collections.Generic; using UnityEngine; using System; namespace Hallucinate.AI { public class ConversationManager : MonoBehaviour { public static ConversationManager Instance { get; private set; } [Header("Settings")] public int maxSimultaneousConversations = 3; public float maxConversationDuration = 120f; // 2 minutes private List activeSessions = new List(); private void Awake() { if (Instance == null) Instance = this; else Destroy(gameObject); } public bool CanStartConversation() { return activeSessions.Count < maxSimultaneousConversations; } public void StartConversation(EnemyAI initiator, EnemyAI responder) { if (!CanStartConversation()) return; ConversationSession session = new ConversationSession(initiator, responder, maxConversationDuration); activeSessions.Add(session); StartCoroutine(RunConversation(session)); } private IEnumerator RunConversation(ConversationSession session) { Debug.Log($"[ConvManager] Starting: {session.initiator.npcName} & {session.responder.npcName}"); // Phase 1: Initiator speaks bool phase1Complete = false; session.RequestDialogue(session.initiator, (success) => phase1Complete = true); float startTime = Time.time; while (!phase1Complete && Time.time < startTime + 10f) yield return null; if (phase1Complete && !session.isInterrupted) { yield return new WaitForSeconds(4f); // Reading time // Phase 2: Responder speaks bool phase2Complete = false; session.RequestDialogue(session.responder, (success) => phase2Complete = true); float phase2StartTime = Time.time; while (!phase2Complete && Time.time < phase2StartTime + 10f) yield return null; } yield return new WaitForSeconds(4f); EndConversation(session); } public void EndConversation(ConversationSession session) { if (activeSessions.Contains(session)) { session.Cleanup(); activeSessions.Remove(session); Debug.Log($"[ConvManager] Ended session. Active: {activeSessions.Count}"); } } public void InterruptConversation(EnemyAI npc) { ConversationSession session = activeSessions.Find(s => s.initiator == npc || s.responder == npc); if (session != null) { session.isInterrupted = true; EndConversation(session); } } } public class ConversationSession { public EnemyAI initiator; public EnemyAI responder; public float durationLimit; public bool isInterrupted; public ConversationSession(EnemyAI initiator, EnemyAI responder, float limit) { this.initiator = initiator; this.responder = responder; this.durationLimit = limit; initiator.isTalking = true; responder.isTalking = true; // Set references for Gizmos and Facing initiator.SetTalkingPartner(responder); responder.SetTalkingPartner(initiator); } public void RequestDialogue(EnemyAI speaker, Action callback) { if (isInterrupted) { callback?.Invoke(false); return; } EnemyAI listener = (speaker == initiator) ? responder : initiator; // Face each other speaker.FaceTarget(listener.transform.position); listener.FaceTarget(speaker.transform.position); string prompt = $"You are {speaker.npcName} talking to {listener.npcName}. Previous context: None. " + "Keep it natural and short."; GeminiService.Instance.GetResponse(speaker.persona, prompt, (json) => { if (isInterrupted) { callback?.Invoke(false); return; } speaker.ProcessDialogueResult(json); callback?.Invoke(true); }); } public void Cleanup() { if (initiator != null) initiator.isTalking = false; if (responder != null) responder.isTalking = false; } } }