using System.Collections; using OctoCore.Navigation.Data; using OctoCore.Preloading; using OctoCore.UI; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; namespace OctoCore.Navigation { /// /// Main menu manager. /// Handles Play, Quit and optional extra buttons. /// Relies on OctoCore.Navigation for scene loading. /// public class OctoMainMenu : MonoBehaviour { [Header("Scene Settings")] [SerializeField, Tooltip("First scene to load on Play")] private OctoSceneData _firstSceneData; [Header("Preloading")] [SerializeField, Tooltip("Optional. If null, the menu will search or create one at runtime.")] private OctoSmartPreloader _preloader; [SerializeField, Tooltip("Preload every scene manifest before loading the first scene.")] private bool _preloadAllBeforePlay = true; [Header("UI Settings")] [SerializeField, Tooltip("Optional panel to show/hide (options, credits...)")] private GameObject _optionalPanel; [SerializeField] private GameObject _loadingScreen; [SerializeField] private GameObject _loadingWheel; [SerializeField] private Button _optionalPanelFirstButton; [SerializeField] private Button _mainPanelDefaultButton; private bool _isLoading = false; private void Awake() { if (_optionalPanel) _optionalPanel.SetActive(false); if (_loadingScreen) _loadingScreen.SetActive(false); Cursor.visible = true; Cursor.lockState = CursorLockMode.None; ResolvePreloader(); } private void Update() { if (_isLoading && _loadingWheel) _loadingWheel.transform.Rotate(0, 0, -200f * Time.deltaTime); } /// /// Load the first scene. Called by the Play button. /// public void OnPlayButton() { if (_isLoading) return; if (!OctoCore.Instance) { Debug.LogError("[OctoMainMenu] OctoCore instance not found"); return; } if (_firstSceneData == null) { Debug.LogError("[OctoMainMenu] FirstSceneData is null"); return; } StartCoroutine(PlayRoutine()); } private IEnumerator PlayRoutine() { _isLoading = true; if (_loadingScreen) _loadingScreen.SetActive(true); yield return null; yield return null; float minimumVisibleLoadingTime = 2f; float timer = 0f; while (timer < minimumVisibleLoadingTime) { timer += Time.deltaTime; yield return null; } if (_preloadAllBeforePlay) { ResolvePreloader(); if (_preloader == null) { Debug.LogError("[OctoMainMenu] OctoSmartPreloader not found and could not be created."); _isLoading = false; yield break; } yield return _preloader.PreloadAllScenesRoutine(); if (!_preloader.IsReady) { Debug.LogWarning("[OctoMainMenu] Preloader finished but IsReady is false. Continuing anyway."); } } _isLoading = false; OctoCore.Instance.Navigation.LoadScene(_firstSceneData); } private void ResolvePreloader() { if (_preloader != null) return; #if UNITY_2023_1_OR_NEWER _preloader = Object.FindFirstObjectByType(); #else _preloader = Object.FindObjectOfType(); #endif if (_preloader != null) return; GameObject preloaderObject = new GameObject("OctoSmartPreloader"); _preloader = preloaderObject.AddComponent(); _preloader.AutoLoadDatabaseFromResources = true; _preloader.DatabaseResourcesPath = "OctoPreload/OctoPreloadDatabase"; _preloader.DontDestroyOnLoadObject = true; // We want to start preloading only when the player presses Play. _preloader.PreloadAllOnStart = false; // Full boot preload mode. _preloader.KeepAllLoadedForEntireGame = true; // Safe defaults. _preloader.ItemsPerFrame = 32; _preloader.WarmupShaderVariants = true; _preloader.WarmupAllShadersAtEnd = false; _preloader.TouchMaterials = true; _preloader.TouchTextures = true; _preloader.TouchMeshes = true; _preloader.TouchAudioClips = false; _preloader.WarmupParticlePrefabs = true; _preloader.LogProgress = true; } /// /// Quit the application. Called by the Quit button. /// public void OnQuitButton() { Application.Quit(); #if UNITY_EDITOR UnityEditor.EditorApplication.isPlaying = false; #endif } /// /// Toggle an optional panel (options, credits...). Called by any extra button. /// public void OnTogglePanel() { if (_optionalPanel) { bool newState = !_optionalPanel.activeSelf; _optionalPanel.SetActive(newState); } } public void SelectMainDefaultButton() { StartCoroutine(SelectAfterFrame(_mainPanelDefaultButton)); } public void SelectDebugFirstButton() { StartCoroutine(SelectAfterFrame(_optionalPanelFirstButton)); } private IEnumerator SelectAfterFrame(Button button) { MultiTargetButton.ClearPersistence(); if (button != null && EventSystem.current != null) EventSystem.current.SetSelectedGameObject(button.gameObject); OctoMainMenu.IgnoreMouseUntil = Time.time + 0.1f; yield break; } // Exposed so MultiTargetButton can check before activating on hover. public static float IgnoreMouseUntil = 0f; public void ClearButtonPersistence() { MultiTargetButton.ClearPersistence(); } } }