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");
}
}
///
/// Displays the booster pack reward UI and grants the specified number of booster packs.
/// This method is awaitable and handles input mode switching automatically.
/// Supports awarding 1 to N booster packs with animated visuals.
///
/// Number of booster packs to grant
/// Task that completes when the UI sequence is finished
public async System.Threading.Tasks.Task GiveBoosterPacksAsync(int count)
{
Logging.Debug($"[GameManager] GiveBoosterPacksAsync called with count: {count}");
// Get prefab from settings
var settings = GetSettingsObject();
if (settings?.BoosterPackRewardPrefab == null)
{
Logging.Warning("[GameManager] BoosterPackRewardPrefab not set in CardSystemSettings. Cannot show reward UI.");
return;
}
// Find main canvas
GameObject mainCanvasObj = GameObject.FindGameObjectWithTag("MainCanvas");
if (mainCanvasObj == null)
{
Logging.Warning("[GameManager] MainCanvas not found in scene. Cannot show reward UI.");
return;
}
// Switch to UI input mode
InputManager.Instance?.SetInputMode(InputMode.UI);
Logging.Debug("[GameManager] Switched to UI input mode");
// Instantiate UI prefab
GameObject uiInstance = Instantiate(settings.BoosterPackRewardPrefab);
var component = uiInstance.GetComponent();
if (component == null)
{
Logging.Warning("[GameManager] BoosterPackRewardPrefab does not have MinigameBoosterGiver component!");
Destroy(uiInstance);
InputManager.Instance?.SetInputMode(InputMode.GameAndUI);
return;
}
// Parent to main canvas and set stretch-fill anchoring
RectTransform rectTransform = uiInstance.GetComponent();
if (rectTransform != null)
{
rectTransform.SetParent(mainCanvasObj.transform, false);
// Set anchors to stretch-fill (all corners)
rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
rectTransform.offsetMin = Vector2.zero;
rectTransform.offsetMax = Vector2.zero;
Logging.Debug("[GameManager] UI parented to MainCanvas with stretch-fill anchoring");
}
else
{
// Fallback: just parent without RectTransform adjustments
uiInstance.transform.SetParent(mainCanvasObj.transform, false);
Logging.Warning("[GameManager] UI does not have RectTransform, parented without anchor adjustment");
}
// Create TaskCompletionSource for awaiting
var tcs = new System.Threading.Tasks.TaskCompletionSource();
// Initialize the component with pack count
component.Initialize(count);
// Call GiveBooster to start the sequence
component.GiveBooster(() =>
{
Logging.Debug("[GameManager] Booster reward UI sequence completed");
Destroy(uiInstance);
tcs.SetResult(true);
});
// Await completion
await tcs.Task;
// Restore input mode to GameAndUI (default gameplay mode)
InputManager.Instance?.SetInputMode(InputMode.GameAndUI);
Logging.Debug("[GameManager] Restored to GameAndUI input mode");
}
// 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;
}
}