using System; using System.Collections.Generic; using AppleHills.Core.Interfaces; using AppleHills.Core.Settings; using Bootstrap; using UI; using UnityEngine; namespace Core { /// /// Singleton manager for global game state and settings. Provides accessors for various gameplay parameters. /// public class GameManager : MonoBehaviour { // Singleton implementation private static GameManager _instance; public static GameManager Instance => _instance; private static bool _isQuitting = false; private bool _settingsLoaded = false; private bool _developerSettingsLoaded = false; // Pausable implementation private bool _isPaused = false; public bool IsPaused => _isPaused; // List of pausable components that have registered with the GameManager private List _pausableComponents = new List(); // Events for pause state changes public event Action OnGamePaused; public event Action OnGameResumed; void Awake() { _instance = this; // Create settings providers if it doesn't exist SettingsProvider.Instance.gameObject.name = "Settings Provider"; DeveloperSettingsProvider.Instance.gameObject.name = "Developer Settings Provider"; // Load all settings synchronously during Awake InitializeSettings(); InitializeDeveloperSettings(); // Register for post-boot initialization BootCompletionService.RegisterInitAction(InitializePostBoot); // DontDestroyOnLoad(gameObject); } private void InitializePostBoot() { // For post-boot correct initialization order } /// /// Register a component as pausable, so it receives pause/resume notifications /// /// The pausable component to register public void RegisterPausableComponent(IPausable component) { if (component != null && !_pausableComponents.Contains(component)) { _pausableComponents.Add(component); // If the game is already paused, pause the component immediately if (_isPaused) { component.Pause(); } Logging.Debug($"[GameManager] Registered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}"); } } /// /// Unregister a pausable component /// /// The pausable component to unregister public void UnregisterPausableComponent(IPausable component) { if (component != null && _pausableComponents.Contains(component)) { _pausableComponents.Remove(component); Logging.Debug($"[GameManager] Unregistered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}"); } } // TODO: Revisit this with proper pause menu request implementation public void RequestGamePause() { PauseGame(); } public void RequestGameResume() { ResumeGame(); } /// /// Pause the game and notify all registered pausable components /// private void PauseGame() { if (_isPaused) return; // Already paused _isPaused = true; // Pause all registered components foreach (var component in _pausableComponents) { component.Pause(); } // Broadcast pause event OnGamePaused?.Invoke(); Logging.Debug($"[GameManager] Game paused. Paused {_pausableComponents.Count} components."); } /// /// Resume the game and notify all registered pausable components /// private void ResumeGame() { if (!_isPaused) return; // Already running _isPaused = false; // Resume all registered components foreach (var component in _pausableComponents) { component.DoResume(); } // Broadcast resume event OnGameResumed?.Invoke(); Logging.Debug($"[GameManager] Game resumed. Resumed {_pausableComponents.Count} components."); } private void InitializeSettings() { Logging.Debug("Starting settings initialization..."); // Load settings synchronously var playerSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); var interactionSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); var minigameSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); // Register settings with service locator if (playerSettings != null) { ServiceLocator.Register(playerSettings); Logging.Debug("PlayerFollowerSettings registered successfully"); } else { Debug.LogError("Failed to load PlayerFollowerSettings"); } if (interactionSettings != null) { ServiceLocator.Register(interactionSettings); Logging.Debug("InteractionSettings registered successfully"); } else { Debug.LogError("Failed to load InteractionSettings"); } if (minigameSettings != null) { ServiceLocator.Register(minigameSettings); Logging.Debug("MinigameSettings registered successfully"); } else { Debug.LogError("Failed to load MinigameSettings"); } // Log success _settingsLoaded = playerSettings != null && interactionSettings != null && minigameSettings != null; if (_settingsLoaded) { Logging.Debug("All settings loaded and registered with ServiceLocator"); } else { Logging.Warning("Some settings failed to load - check that all settings assets exist and are marked as Addressables"); } } /// /// Check for and initialize developer settings. /// private void InitializeDeveloperSettings() { Logging.Debug("Starting developer settings initialization..."); // Load developer settings var divingDevSettings = DeveloperSettingsProvider.Instance.GetSettings(); _developerSettingsLoaded = divingDevSettings != null; if (_developerSettingsLoaded) { Logging.Debug("All developer settings loaded successfully"); } else { Logging.Warning("Some developer settings failed to load"); } } void OnApplicationQuit() { _isQuitting = true; ServiceLocator.Clear(); } // Helper method to get settings private T GetSettings() where T : class { return ServiceLocator.Get(); } /// /// Returns the entire settings object of specified type. /// /// Type of settings to retrieve /// The settings object or null if not found public static T GetSettingsObject() where T : class { return Instance?.GetSettings(); } /// /// Returns the developer settings object of specified type. /// /// Type of developer settings to retrieve /// The developer settings object or null if not found public static T GetDeveloperSettings() where T : BaseDeveloperSettings { return DeveloperSettingsProvider.Instance?.GetSettings(); } // LEFTOVER LEGACY SETTINGS public float PlayerStopDistance => GetSettings()?.PlayerStopDistance ?? 6.0f; public float PlayerStopDistanceDirectInteraction => GetSettings()?.PlayerStopDistanceDirectInteraction ?? 2.0f; public float DefaultPuzzlePromptRange => GetSettings()?.DefaultPuzzlePromptRange ?? 3.0f; } }