using NightclubArcadia.UI; using UnityEngine; namespace NightclubArcadia.Cinematics { public enum CameraState { Follow, ManualPan, PointOfInterest, Reveal, Dialogue, Menu } /// /// Resolves which camera subsystem may write each frame. /// public sealed class CameraStateDirector : MonoBehaviour { [SerializeField] CameraRigAnchor anchor; [SerializeField] float dialogueRecenterTime = 0.5f; int activeRevealCount; float dialogueRecenterTimer; bool wasDialogueActive; bool wasMenuOpen; public CameraState Current { get; private set; } = CameraState.Follow; public bool AllowsManualPan => Current != CameraState.Dialogue && Current != CameraState.Menu && Current != CameraState.Reveal; public bool AllowsPointOfInterest => Current == CameraState.Follow || Current == CameraState.PointOfInterest; void Awake() { if (anchor == null) { anchor = GetComponent(); } } void Start() { if (UIStateController.Instance != null) { UIStateController.Instance.StateChanged += OnUiStateChanged; } ResolveState(false); } void OnDestroy() { if (UIStateController.Instance != null) { UIStateController.Instance.StateChanged -= OnUiStateChanged; } } void Update() { ResolveState(true); if (Current == CameraState.Dialogue && anchor != null) { dialogueRecenterTimer += Time.deltaTime; var t = Mathf.Clamp01(dialogueRecenterTimer / dialogueRecenterTime); anchor.ManualPanOffset = Vector3.Lerp(anchor.ManualPanOffset, Vector3.zero, t); } } void OnUiStateChanged() => ResolveState(true); public void RegisterReveal() => activeRevealCount++; public void UnregisterReveal() => activeRevealCount = Mathf.Max(0, activeRevealCount - 1); void ResolveState(bool allowManualPanPromotion) { var ui = UIStateController.Instance; var menuOpen = ui != null && ui.IsMenuOpen; var dialogueActive = ui != null && ui.IsDialogueActive; if (menuOpen) { Current = CameraState.Menu; } else if (dialogueActive) { if (!wasDialogueActive) { dialogueRecenterTimer = 0f; } Current = CameraState.Dialogue; } else if (activeRevealCount > 0) { Current = CameraState.Reveal; } else if (allowManualPanPromotion && IsManualPanActive()) { Current = CameraState.ManualPan; } else if (HasActivePointOfInterest()) { Current = CameraState.PointOfInterest; } else { Current = CameraState.Follow; } wasDialogueActive = dialogueActive; wasMenuOpen = menuOpen; } bool IsManualPanActive() { var pan = GetComponent(); return pan != null && pan.IsManualPanActive; } bool HasActivePointOfInterest() { var director = GetComponent(); return director != null && director.HasActivePoi; } } }