using System; using System.Collections.Generic; using AppleHills.Core.Interfaces; using AppleHills.Core.Settings; using Core.Lifecycle; using Core.Settings; using Input; using Minigames.Airplane.Settings; using Minigames.FortFight.Core; using UnityEngine; namespace Core { /// /// Singleton manager for global game state and settings. Provides accessors for various gameplay parameters. /// public class GameManager : ManagedBehaviour { // Singleton implementation private static GameManager _instance; public static GameManager Instance => _instance; private bool _settingsLoaded; private bool _developerSettingsLoaded; // Pausable implementation (counter-based) private int _pauseCount; public bool IsPaused => _pauseCount > 0; // List of pausable components that have registered with the GameManager private List _pausableComponents = new List(); private LogVerbosity _settingsLogVerbosity = LogVerbosity.Warning; private LogVerbosity _managerLogVerbosity = LogVerbosity.Warning; // Events for pause state changes public event Action OnGamePaused; public event Action OnGameResumed; internal override void OnManagedAwake() { // Set instance immediately (early initialization) _instance = this; // Create settings providers - must happen in OnManagedAwake so other managers can access settings in their ManagedStart SettingsProvider.Instance.gameObject.name = "Settings Provider"; DeveloperSettingsProvider.Instance.gameObject.name = "Developer Settings Provider"; // Load all settings synchronously - critical infrastructure for other managers InitializeSettings(); InitializeDeveloperSettings(); // Load verbosity settings early _settingsLogVerbosity = DeveloperSettingsProvider.Instance.GetSettings().settingsLogVerbosity; _managerLogVerbosity = DeveloperSettingsProvider.Instance.GetSettings().gameManagerLogVerbosity; } internal override void OnManagedStart() { // Settings are already initialized in OnManagedAwake() // This is available for future initialization that depends on other managers } /// /// 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($"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($"Unregistered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}"); } } /// /// Request a pause. Multiple systems can request pause; the game only resumes when all requests are released. /// /// Optional object requesting the pause (for logging) public void RequestPause(object requester = null) { _pauseCount++; if (_pauseCount == 1) { ApplyPause(true); } Logging.Debug($"Pause requested by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}"); } /// /// Release a previously requested pause. If this brings the pause count to zero the game resumes. /// /// Optional object releasing the pause (for logging) public void ReleasePause(object requester = null) { _pauseCount = Mathf.Max(0, _pauseCount - 1); if (_pauseCount == 0) { ApplyPause(false); } Logging.Debug($"Pause released by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}"); } /// /// Apply pause/resume state to registered components and global systems. /// /// True to pause; false to resume private void ApplyPause(bool shouldPause) { // Only affect timescale if debug setting allows it var debugSettings = GetDeveloperSettings(); if (debugSettings != null && debugSettings.PauseTimeOnPauseGame) { Time.timeScale = shouldPause ? 0f : 1f; } // Notify registered components foreach (var component in _pausableComponents) { if (shouldPause) component.Pause(); else component.DoResume(); } // Fire events if (shouldPause) { // TODO: Stop input here? InputManager.Instance.SetInputMode(InputMode.UI); OnGamePaused?.Invoke(); } else { // TODO: Release input here? InputManager.Instance.SetInputMode(InputMode.GameAndUI); OnGameResumed?.Invoke(); } Logging.Debug($"Game {(shouldPause ? "paused" : "resumed")}. Paused {_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(); var cardSystemSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); var sortingGameSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); var birdPooperSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); var statueDressupSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); var fortFightSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); var airplaneSettings = 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"); } if (cardSystemSettings != null) { ServiceLocator.Register(cardSystemSettings); Logging.Debug("CardSystemSettings registered successfully"); } else { Debug.LogError("Failed to load CardSystemSettings"); } if (sortingGameSettings != null) { ServiceLocator.Register(sortingGameSettings); Logging.Debug("CardSortingSettings registered successfully"); } else { Debug.LogError("Failed to load CardSystemSettings"); } if (birdPooperSettings != null) { ServiceLocator.Register(birdPooperSettings); Logging.Debug("BirdPooperSettings registered successfully"); } else { Debug.LogError("Failed to load BirdPooperSettings"); } if (statueDressupSettings != null) { ServiceLocator.Register(statueDressupSettings); Logging.Debug("StatueDressupSettings registered successfully"); } else { Debug.LogError("Failed to load StatueDressupSettings"); } if (fortFightSettings != null) { ServiceLocator.Register(fortFightSettings); Logging.Debug("FortFightSettings registered successfully"); } else { Debug.LogError("Failed to load FortFightSettings"); } if (airplaneSettings != null) { ServiceLocator.Register(airplaneSettings); Logging.Debug("AirplaneSettings registered successfully"); } else { Debug.LogError("Failed to load AirplaneSettings"); } // Log success _settingsLoaded = playerSettings != null && interactionSettings != null && minigameSettings != null && cardSystemSettings != null && birdPooperSettings != null && statueDressupSettings != null && fortFightSettings != null && sortingGameSettings != null && airplaneSettings != 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(); var debugSettings = DeveloperSettingsProvider.Instance.GetSettings(); _developerSettingsLoaded = divingDevSettings != null && debugSettings != null; if (_developerSettingsLoaded) { Logging.Debug("All developer settings loaded successfully"); } else { Logging.Warning("Some developer settings failed to load"); } } // 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; // Fort Fight Settings public float WeakPointExplosionRadius => GetSettings()?.WeakPointExplosionRadius ?? 2.5f; } }