using UnityEngine; using System; using Unheimlich.Data.Dialogues; using Unheimlich.Gameplay.Player; using Unheimlich.UI; using Unheimlich.Interactions.Dialogues; using System.Collections.Generic; namespace Unheimlich.Managers { public class DialogueManager : MonoBehaviour { public static DialogueManager Instance { get; private set; } [Header("State")] public bool IsInDialogue = false; [Header("Focus Settings")] [SerializeField] private LayerMask _choiceLayer; [SerializeField] private float _focusRange = 5f; [SerializeField] private float _focusTime = 1.5f; [SerializeField] private float _raycastInterval = 0.05f; [Header("Focus Stabilization")] [SerializeField] private float _recoveryDuration = 0.5f; [SerializeField] private float _focusRetentionTime = 0.1f; [SerializeField] private float _shakeIntensity = 30f; public DialogueAnimationSettings CurrentAnimationSettings => _currentAnimationSettings; public bool IsDialogueActive => IsInDialogue; public Transform CurrentChoiceAnchor => _currentChoiceAnchor; public DialogueUI CurrentDialogueUI => _activeDialogueUI; public object CurrentOwner { get; private set; } public DialogueConversation CurrentConversation => _currentConversation; public DialogueNode CurrentNode => _currentNode; public event Action OnChoiceMade; public event Action OnDialogueCompleted; private DialogueFocusSystem _focusSystem; private Camera _mainCamera; private PlayerLook _playerLook; private DialogueAnimationSettings _currentAnimationSettings; private DialogueUI _activeDialogueUI; private Transform _currentChoiceAnchor; private DialogueConversation _currentConversation; private DialogueNode _currentNode; private float _autoCloseTimer = 0f; private bool _isWaitingForAutoClose = false; private void Awake() { if (Instance != null && Instance != this) { Destroy(this); return; } Instance = this; } private void Start() { _playerLook = PlayerLook.Instance; _mainCamera = Camera.main; _focusSystem = new DialogueFocusSystem( this, _mainCamera, _playerLook, _choiceLayer, _focusRange, _focusTime, _raycastInterval, _recoveryDuration, _focusRetentionTime, _shakeIntensity ); _focusSystem.OnChoiceMade += (choice) => OnChoiceMade?.Invoke(choice); } private void Update() { if (!IsInDialogue) return; if (!_activeDialogueUI.AreChoicesVisible()) { if (_isWaitingForAutoClose && _currentNode != null && _currentNode.AutoCloseDelay > 0f) { _autoCloseTimer += Time.deltaTime; if (_autoCloseTimer >= _currentNode.AutoCloseDelay) { _activeDialogueUI.FadeOutAndHide(() => { OnDialogueCompleted?.Invoke(CurrentOwner); CleanupDialogue(); }); IsInDialogue = false; _isWaitingForAutoClose = false; if (DialogueBlurEffectURP.Instance != null) { DialogueBlurEffectURP.Instance.HideBlur(); } return; } } if (PlayerInputManager.Instance != null && PlayerInputManager.Instance.InteractAction.action.WasPressedThisFrame()) { EndDialogue(); } } else { _focusSystem?.Update(); } } public void StartConversation(DialogueConversation conversation, Transform choiceAnchor = null, DialogueAnimationSettings animationSettings = null, DialogueUI customDialogueUI = null, object owner = null) { if (conversation == null) { Debug.LogError("Cannot start conversation: conversation is null"); return; } DialogueNode firstNode = conversation.GetFirstNode(); if (firstNode == null) { Debug.LogError($"Cannot start conversation {conversation.name}: no dialogue nodes found"); return; } _currentConversation = conversation; _currentNode = firstNode; _currentChoiceAnchor = choiceAnchor; _currentAnimationSettings = animationSettings; _activeDialogueUI = customDialogueUI; CurrentOwner = owner; ShowCurrentNode(); } private void ShowCurrentNode() { if (_currentNode == null || _currentConversation == null) return; UnlockFocus(); IsInDialogue = true; if(_currentChoiceAnchor == null) { Debug.LogWarning("No choice anchor found!"); return; } Transform anchor = _currentChoiceAnchor; string speakerName = _currentConversation.SpeakerName; string dialogueText = _currentNode.DialogueText; DialogueDisplayMode displayMode = _currentConversation.DisplayMode; _activeDialogueUI.ShowDialogueBox(speakerName, dialogueText, displayMode); _autoCloseTimer = 0f; _isWaitingForAutoClose = false; if (_currentNode.Choices != null && _currentNode.Choices.Count > 0) { List validChoices = new List(); foreach (var choiceData in _currentNode.Choices) { if (choiceData != null && choiceData.IsValid()) validChoices.Add(choiceData); } if (validChoices.Count > 0) _activeDialogueUI.ShowChoices(validChoices, anchor, _currentAnimationSettings); } else _isWaitingForAutoClose = true; } public void GoToNextNode(DialogueNode nextNode) { if (nextNode == null) { OnDialogueCompleted?.Invoke(CurrentOwner); EndDialogue(); return; } UnlockFocus(); if (_activeDialogueUI != null) _activeDialogueUI.HideChoices(); _currentNode = nextNode; ShowCurrentNode(); } public void EndDialogue() { IsInDialogue = false; if (_activeDialogueUI != null) _activeDialogueUI.HideDialogueBox(); if (DialogueBlurEffectURP.Instance != null) DialogueBlurEffectURP.Instance.HideBlur(); CleanupDialogue(); } private void CleanupDialogue() { _currentChoiceAnchor = null; _currentAnimationSettings = null; _activeDialogueUI = null; CurrentOwner = null; _currentConversation = null; _currentNode = null; _autoCloseTimer = 0f; _isWaitingForAutoClose = false; } public void UnlockFocus() { _focusSystem?.UnlockFocus(); } } }