2025-10-24 11:09:32 +00:00
|
|
|
|
using System;
|
2025-10-08 12:36:08 +02:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using AppleHills.Core.Interfaces;
|
2025-10-24 11:09:32 +00:00
|
|
|
|
using AppleHills.Core.Settings;
|
2025-11-07 15:38:31 +00:00
|
|
|
|
using Core.Lifecycle;
|
2025-10-28 14:31:17 +01:00
|
|
|
|
using Core.Settings;
|
2025-10-24 11:09:32 +00:00
|
|
|
|
using Input;
|
|
|
|
|
|
using UnityEngine;
|
2025-09-04 00:00:46 +02:00
|
|
|
|
|
2025-10-24 11:09:32 +00:00
|
|
|
|
namespace Core
|
2025-09-04 00:00:46 +02:00
|
|
|
|
{
|
2025-09-06 21:01:54 +02:00
|
|
|
|
/// <summary>
|
2025-10-24 11:09:32 +00:00
|
|
|
|
/// Singleton manager for global game state and settings. Provides accessors for various gameplay parameters.
|
2025-09-06 21:01:54 +02:00
|
|
|
|
/// </summary>
|
2025-11-07 15:38:31 +00:00
|
|
|
|
public class GameManager : ManagedBehaviour
|
2025-10-24 11:09:32 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Singleton implementation
|
|
|
|
|
|
private static GameManager _instance;
|
|
|
|
|
|
public static GameManager Instance => _instance;
|
2025-09-04 00:00:46 +02:00
|
|
|
|
|
2025-10-24 11:09:32 +00:00
|
|
|
|
private bool _settingsLoaded;
|
|
|
|
|
|
private bool _developerSettingsLoaded;
|
|
|
|
|
|
|
|
|
|
|
|
// Pausable implementation (counter-based)
|
|
|
|
|
|
private int _pauseCount;
|
|
|
|
|
|
public bool IsPaused => _pauseCount > 0;
|
2025-10-08 12:36:08 +02:00
|
|
|
|
|
2025-10-24 11:09:32 +00:00
|
|
|
|
// List of pausable components that have registered with the GameManager
|
|
|
|
|
|
private List<IPausable> _pausableComponents = new List<IPausable>();
|
2025-10-28 14:31:17 +01:00
|
|
|
|
private LogVerbosity _settingsLogVerbosity = LogVerbosity.Warning;
|
|
|
|
|
|
private LogVerbosity _managerLogVerbosity = LogVerbosity.Warning;
|
|
|
|
|
|
|
2025-10-24 11:09:32 +00:00
|
|
|
|
// Events for pause state changes
|
|
|
|
|
|
public event Action OnGamePaused;
|
|
|
|
|
|
public event Action OnGameResumed;
|
2025-09-04 00:00:46 +02:00
|
|
|
|
|
2025-11-07 15:38:31 +00:00
|
|
|
|
|
2025-11-11 08:48:29 +00:00
|
|
|
|
internal override void OnManagedAwake()
|
2025-10-24 11:09:32 +00:00
|
|
|
|
{
|
2025-11-11 08:48:29 +00:00
|
|
|
|
// Set instance immediately (early initialization)
|
2025-10-24 11:09:32 +00:00
|
|
|
|
_instance = this;
|
2025-11-07 15:38:31 +00:00
|
|
|
|
|
2025-11-11 08:48:29 +00:00
|
|
|
|
// Create settings providers - must happen in OnManagedAwake so other managers can access settings in their ManagedStart
|
2025-10-24 11:09:32 +00:00
|
|
|
|
SettingsProvider.Instance.gameObject.name = "Settings Provider";
|
|
|
|
|
|
DeveloperSettingsProvider.Instance.gameObject.name = "Developer Settings Provider";
|
2025-09-24 13:33:43 +00:00
|
|
|
|
|
2025-11-07 15:38:31 +00:00
|
|
|
|
// Load all settings synchronously - critical infrastructure for other managers
|
2025-10-24 11:09:32 +00:00
|
|
|
|
InitializeSettings();
|
|
|
|
|
|
InitializeDeveloperSettings();
|
2025-11-07 15:38:31 +00:00
|
|
|
|
|
|
|
|
|
|
// Load verbosity settings early
|
2025-10-28 14:31:17 +01:00
|
|
|
|
_settingsLogVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().settingsLogVerbosity;
|
|
|
|
|
|
_managerLogVerbosity = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().gameManagerLogVerbosity;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-11 08:48:29 +00:00
|
|
|
|
internal override void OnManagedStart()
|
2025-10-08 12:36:08 +02:00
|
|
|
|
{
|
2025-11-11 08:48:29 +00:00
|
|
|
|
// Settings are already initialized in OnManagedAwake()
|
2025-11-07 15:38:31 +00:00
|
|
|
|
// This is available for future initialization that depends on other managers
|
2025-10-08 12:36:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-24 11:09:32 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Register a component as pausable, so it receives pause/resume notifications
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="component">The pausable component to register</param>
|
|
|
|
|
|
public void RegisterPausableComponent(IPausable component)
|
2025-10-08 12:36:08 +02:00
|
|
|
|
{
|
2025-10-24 11:09:32 +00:00
|
|
|
|
if (component != null && !_pausableComponents.Contains(component))
|
2025-10-08 12:36:08 +02:00
|
|
|
|
{
|
2025-10-24 11:09:32 +00:00
|
|
|
|
_pausableComponents.Add(component);
|
2025-10-08 12:36:08 +02:00
|
|
|
|
|
2025-10-24 11:09:32 +00:00
|
|
|
|
// If the game is already paused, pause the component immediately
|
|
|
|
|
|
if (IsPaused)
|
|
|
|
|
|
{
|
|
|
|
|
|
component.Pause();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-11 08:48:29 +00:00
|
|
|
|
Logging.Debug($"Registered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}");
|
2025-10-24 11:09:32 +00:00
|
|
|
|
}
|
2025-10-08 12:36:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-24 11:09:32 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Unregister a pausable component
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="component">The pausable component to unregister</param>
|
|
|
|
|
|
public void UnregisterPausableComponent(IPausable component)
|
2025-10-08 12:36:08 +02:00
|
|
|
|
{
|
2025-10-24 11:09:32 +00:00
|
|
|
|
if (component != null && _pausableComponents.Contains(component))
|
|
|
|
|
|
{
|
|
|
|
|
|
_pausableComponents.Remove(component);
|
2025-11-11 08:48:29 +00:00
|
|
|
|
Logging.Debug($"Unregistered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}");
|
2025-10-24 11:09:32 +00:00
|
|
|
|
}
|
2025-10-08 12:36:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-24 11:09:32 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Request a pause. Multiple systems can request pause; the game only resumes when all requests are released.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="requester">Optional object requesting the pause (for logging)</param>
|
|
|
|
|
|
public void RequestPause(object requester = null)
|
2025-10-08 12:36:08 +02:00
|
|
|
|
{
|
2025-10-24 11:09:32 +00:00
|
|
|
|
_pauseCount++;
|
|
|
|
|
|
if (_pauseCount == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
ApplyPause(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-11 08:48:29 +00:00
|
|
|
|
Logging.Debug($"Pause requested by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
|
2025-10-08 12:36:08 +02:00
|
|
|
|
}
|
2025-09-24 13:33:43 +00:00
|
|
|
|
|
2025-10-24 11:09:32 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Release a previously requested pause. If this brings the pause count to zero the game resumes.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="requester">Optional object releasing the pause (for logging)</param>
|
|
|
|
|
|
public void ReleasePause(object requester = null)
|
2025-09-12 16:47:45 +02:00
|
|
|
|
{
|
2025-10-24 11:09:32 +00:00
|
|
|
|
_pauseCount = Mathf.Max(0, _pauseCount - 1);
|
|
|
|
|
|
if (_pauseCount == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
ApplyPause(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-11 08:48:29 +00:00
|
|
|
|
Logging.Debug($"Pause released by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}");
|
2025-09-24 13:33:43 +00:00
|
|
|
|
}
|
2025-10-24 11:09:32 +00:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Apply pause/resume state to registered components and global systems.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="shouldPause">True to pause; false to resume</param>
|
|
|
|
|
|
private void ApplyPause(bool shouldPause)
|
2025-09-24 13:33:43 +00:00
|
|
|
|
{
|
2025-10-27 15:21:23 +01:00
|
|
|
|
// Only affect timescale if debug setting allows it
|
|
|
|
|
|
var debugSettings = GetDeveloperSettings<DebugSettings>();
|
|
|
|
|
|
if (debugSettings != null && debugSettings.PauseTimeOnPauseGame)
|
|
|
|
|
|
{
|
|
|
|
|
|
Time.timeScale = shouldPause ? 0f : 1f;
|
|
|
|
|
|
}
|
2025-09-24 13:33:43 +00:00
|
|
|
|
|
2025-10-24 11:09:32 +00:00
|
|
|
|
// 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();
|
|
|
|
|
|
}
|
2025-09-24 13:33:43 +00:00
|
|
|
|
|
2025-11-11 08:48:29 +00:00
|
|
|
|
Logging.Debug($"Game {(shouldPause ? "paused" : "resumed")}. Paused {_pausableComponents.Count} components.");
|
2025-09-24 13:33:43 +00:00
|
|
|
|
}
|
2025-10-24 11:09:32 +00:00
|
|
|
|
|
|
|
|
|
|
private void InitializeSettings()
|
2025-09-24 13:33:43 +00:00
|
|
|
|
{
|
2025-11-11 08:48:29 +00:00
|
|
|
|
Logging.Debug("Starting settings initialization...");
|
2025-10-24 11:09:32 +00:00
|
|
|
|
|
|
|
|
|
|
// Load settings synchronously
|
|
|
|
|
|
var playerSettings = SettingsProvider.Instance.LoadSettingsSynchronous<PlayerFollowerSettings>();
|
|
|
|
|
|
var interactionSettings = SettingsProvider.Instance.LoadSettingsSynchronous<InteractionSettings>();
|
|
|
|
|
|
var minigameSettings = SettingsProvider.Instance.LoadSettingsSynchronous<DivingMinigameSettings>();
|
2025-11-18 08:40:59 +00:00
|
|
|
|
var cardSystemSettings = SettingsProvider.Instance.LoadSettingsSynchronous<CardSystemSettings>();
|
2025-11-19 13:56:10 +00:00
|
|
|
|
var sortingGameSettings = SettingsProvider.Instance.LoadSettingsSynchronous<CardSortingSettings>();
|
2025-11-20 01:29:30 +01:00
|
|
|
|
var birdPooperSettings = SettingsProvider.Instance.LoadSettingsSynchronous<BirdPooperSettings>();
|
2025-10-24 11:09:32 +00:00
|
|
|
|
|
|
|
|
|
|
// Register settings with service locator
|
|
|
|
|
|
if (playerSettings != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ServiceLocator.Register<IPlayerFollowerSettings>(playerSettings);
|
2025-11-11 08:48:29 +00:00
|
|
|
|
Logging.Debug("PlayerFollowerSettings registered successfully");
|
2025-10-24 11:09:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("Failed to load PlayerFollowerSettings");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (interactionSettings != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ServiceLocator.Register<IInteractionSettings>(interactionSettings);
|
2025-11-11 08:48:29 +00:00
|
|
|
|
Logging.Debug("InteractionSettings registered successfully");
|
2025-10-24 11:09:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("Failed to load InteractionSettings");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (minigameSettings != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ServiceLocator.Register<IDivingMinigameSettings>(minigameSettings);
|
2025-11-11 08:48:29 +00:00
|
|
|
|
Logging.Debug("MinigameSettings registered successfully");
|
2025-10-24 11:09:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("Failed to load MinigameSettings");
|
|
|
|
|
|
}
|
2025-11-18 08:40:59 +00:00
|
|
|
|
|
|
|
|
|
|
if (cardSystemSettings != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ServiceLocator.Register<ICardSystemSettings>(cardSystemSettings);
|
|
|
|
|
|
Logging.Debug("CardSystemSettings registered successfully");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("Failed to load CardSystemSettings");
|
|
|
|
|
|
}
|
2025-11-19 13:56:10 +00:00
|
|
|
|
|
|
|
|
|
|
if (sortingGameSettings != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ServiceLocator.Register<ICardSortingSettings>(sortingGameSettings);
|
|
|
|
|
|
Logging.Debug("CardSortingSettings registered successfully");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("Failed to load CardSystemSettings");
|
|
|
|
|
|
}
|
2025-11-20 01:29:30 +01:00
|
|
|
|
|
|
|
|
|
|
if (birdPooperSettings != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ServiceLocator.Register<IBirdPooperSettings>(birdPooperSettings);
|
|
|
|
|
|
Logging.Debug("BirdPooperSettings registered successfully");
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("Failed to load BirdPooperSettings");
|
|
|
|
|
|
}
|
2025-10-24 11:09:32 +00:00
|
|
|
|
|
|
|
|
|
|
// Log success
|
2025-11-20 01:29:30 +01:00
|
|
|
|
_settingsLoaded = playerSettings != null && interactionSettings != null && minigameSettings != null && cardSystemSettings != null && birdPooperSettings != null;
|
2025-10-24 11:09:32 +00:00
|
|
|
|
if (_settingsLoaded)
|
|
|
|
|
|
{
|
2025-11-11 08:48:29 +00:00
|
|
|
|
Logging.Debug("All settings loaded and registered with ServiceLocator");
|
2025-10-24 11:09:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Logging.Warning("Some settings failed to load - check that all settings assets exist and are marked as Addressables");
|
|
|
|
|
|
}
|
2025-09-24 13:33:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-24 11:09:32 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Check for and initialize developer settings.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void InitializeDeveloperSettings()
|
2025-09-24 13:33:43 +00:00
|
|
|
|
{
|
2025-11-11 08:48:29 +00:00
|
|
|
|
Logging.Debug("Starting developer settings initialization...");
|
2025-10-24 11:09:32 +00:00
|
|
|
|
|
|
|
|
|
|
// Load developer settings
|
|
|
|
|
|
var divingDevSettings = DeveloperSettingsProvider.Instance.GetSettings<DivingDeveloperSettings>();
|
2025-10-27 15:21:23 +01:00
|
|
|
|
var debugSettings = DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>();
|
2025-10-24 11:09:32 +00:00
|
|
|
|
|
2025-10-27 15:21:23 +01:00
|
|
|
|
_developerSettingsLoaded = divingDevSettings != null && debugSettings != null;
|
2025-10-24 11:09:32 +00:00
|
|
|
|
|
|
|
|
|
|
if (_developerSettingsLoaded)
|
|
|
|
|
|
{
|
2025-11-11 08:48:29 +00:00
|
|
|
|
Logging.Debug("All developer settings loaded successfully");
|
2025-10-24 11:09:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Logging.Warning("Some developer settings failed to load");
|
|
|
|
|
|
}
|
2025-09-24 13:33:43 +00:00
|
|
|
|
}
|
2025-10-24 11:09:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Helper method to get settings
|
|
|
|
|
|
private T GetSettings<T>() where T : class
|
2025-09-24 13:33:43 +00:00
|
|
|
|
{
|
2025-10-24 11:09:32 +00:00
|
|
|
|
return ServiceLocator.Get<T>();
|
2025-09-24 13:33:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-24 11:09:32 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Returns the entire settings object of specified type.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <typeparam name="T">Type of settings to retrieve</typeparam>
|
|
|
|
|
|
/// <returns>The settings object or null if not found</returns>
|
|
|
|
|
|
public static T GetSettingsObject<T>() where T : class
|
2025-09-24 13:33:43 +00:00
|
|
|
|
{
|
2025-10-24 11:09:32 +00:00
|
|
|
|
return Instance?.GetSettings<T>();
|
2025-09-24 13:33:43 +00:00
|
|
|
|
}
|
2025-10-24 11:09:32 +00:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Returns the developer settings object of specified type.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <typeparam name="T">Type of developer settings to retrieve</typeparam>
|
|
|
|
|
|
/// <returns>The developer settings object or null if not found</returns>
|
|
|
|
|
|
public static T GetDeveloperSettings<T>() where T : BaseDeveloperSettings
|
2025-09-24 13:33:43 +00:00
|
|
|
|
{
|
2025-10-24 11:09:32 +00:00
|
|
|
|
return DeveloperSettingsProvider.Instance?.GetSettings<T>();
|
2025-09-12 16:47:45 +02:00
|
|
|
|
}
|
2025-10-28 14:31:17 +01:00
|
|
|
|
|
2025-10-24 11:09:32 +00:00
|
|
|
|
// LEFTOVER LEGACY SETTINGS
|
|
|
|
|
|
public float PlayerStopDistance => GetSettings<IInteractionSettings>()?.PlayerStopDistance ?? 6.0f;
|
|
|
|
|
|
public float PlayerStopDistanceDirectInteraction => GetSettings<IInteractionSettings>()?.PlayerStopDistanceDirectInteraction ?? 2.0f;
|
|
|
|
|
|
public float DefaultPuzzlePromptRange => GetSettings<IInteractionSettings>()?.DefaultPuzzlePromptRange ?? 3.0f;
|
2025-09-04 00:00:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|