diff --git a/Assets/Scripts/Bootstrap/BootCompletionService.cs b/Assets/Scripts/Bootstrap/BootCompletionService.cs index 71be1e09..c74f6c4e 100644 --- a/Assets/Scripts/Bootstrap/BootCompletionService.cs +++ b/Assets/Scripts/Bootstrap/BootCompletionService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; diff --git a/Assets/Scripts/Bootstrap/CustomBoot.cs b/Assets/Scripts/Bootstrap/CustomBoot.cs index 342e1c8e..6ff31ccc 100644 --- a/Assets/Scripts/Bootstrap/CustomBoot.cs +++ b/Assets/Scripts/Bootstrap/CustomBoot.cs @@ -98,14 +98,9 @@ namespace Bootstrap // Notify the BootCompletionService that boot is complete if (Application.isPlaying) { - // We use reflection to avoid direct reference in case the service is disabled/removed - var type = System.Type.GetType("Bootstrap.BootCompletionService, Assembly-CSharp"); - if (type != null) - { - var method = type.GetMethod("HandleBootCompleted", - System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); - method?.Invoke(null, null); - } + // Direct call to boot completion service + Debug.Log("[CustomBoot] Calling BootCompletionService.HandleBootCompleted()"); + BootCompletionService.HandleBootCompleted(); } } @@ -123,14 +118,9 @@ namespace Bootstrap // Notify the BootCompletionService that boot is complete if (Application.isPlaying) { - // We use reflection to avoid direct reference in case the service is disabled/removed - var type = System.Type.GetType("Bootstrap.BootCompletionService, Assembly-CSharp"); - if (type != null) - { - var method = type.GetMethod("HandleBootCompleted", - System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); - method?.Invoke(null, null); - } + // Direct call to boot completion service + Debug.Log("[CustomBoot] Calling BootCompletionService.HandleBootCompleted()"); + BootCompletionService.HandleBootCompleted(); } } diff --git a/Assets/Scripts/Cinematics/SkipCinematic.cs b/Assets/Scripts/Cinematics/SkipCinematic.cs index 17e546a9..44094cca 100644 --- a/Assets/Scripts/Cinematics/SkipCinematic.cs +++ b/Assets/Scripts/Cinematics/SkipCinematic.cs @@ -1,3 +1,4 @@ +using Bootstrap; using Core; using Input; using UnityEngine; @@ -14,7 +15,14 @@ namespace Cinematics private float _holdStartTime; private bool _isHolding; private bool _skipPerformed; + private bool _initialized = false; + void Awake() + { + // Register for post-boot initialization + BootCompletionService.RegisterInitAction(InitializePostBoot); + } + void Start() { // Reset the progress bar @@ -23,32 +31,93 @@ namespace Cinematics radialProgressBar.fillAmount = 0f; } } - + void OnEnable() { - if (CinematicsManager.Instance.IsCinematicPlaying) - HandleCinematicStarted(); - - CinematicsManager.Instance.OnCinematicStarted += HandleCinematicStarted; - CinematicsManager.Instance.OnCinematicStopped += HandleCinematicStopped; + // Only handle the case where the GameObject is enabled after boot is already complete + if (!_initialized && BootCompletionService.IsBootComplete) + { + // Boot is already complete but we haven't initialized yet, do it now + Logging.Debug("[SkipCinematic] OnEnable: Boot already complete, initializing now"); + InitializePostBoot(); + } + else if (_initialized) + { + // If we're already initialized, just ensure subscriptions are active + SubscribeToCinematicsEvents(); + } + // If boot isn't complete, our InitializePostBoot method will be called via the BootCompletionService } void OnDisable() { - CinematicsManager.Instance.OnCinematicStarted -= HandleCinematicStarted; - CinematicsManager.Instance.OnCinematicStopped -= HandleCinematicStopped; - // If still registered, unregister input override - InputManager.Instance.UnregisterOverrideConsumer(this); + // Clean up subscriptions regardless of initialization state + UnsubscribeFromCinematicsEvents(); + } + + private void InitializePostBoot() + { + // Safe initialization of manager dependencies after boot is complete + if (_initialized) + return; + + _initialized = true; + + // Subscribe to CinematicsManager events now that boot is complete + SubscribeToCinematicsEvents(); + + Logging.Debug("[SkipCinematic] Post-boot initialization complete"); + } + + private void SubscribeToCinematicsEvents() + { + if (CinematicsManager.Instance == null) return; + + // First unsubscribe to prevent duplicate subscriptions + UnsubscribeFromCinematicsEvents(); + + // Now subscribe + CinematicsManager.Instance.OnCinematicStarted += HandleCinematicStarted; + CinematicsManager.Instance.OnCinematicStopped += HandleCinematicStopped; + + // Check if a cinematic is already playing + if (CinematicsManager.Instance.IsCinematicPlaying) + HandleCinematicStarted(); + + Logging.Debug("[SkipCinematic] Subscribed to cinematics events"); + } + + private void UnsubscribeFromCinematicsEvents() + { + if (CinematicsManager.Instance != null) + { + CinematicsManager.Instance.OnCinematicStarted -= HandleCinematicStarted; + CinematicsManager.Instance.OnCinematicStopped -= HandleCinematicStopped; + Logging.Debug("[SkipCinematic] Unsubscribed from cinematics events"); + } + + // If still registered as an input override consumer, unregister + if (InputManager.Instance != null) + { + InputManager.Instance.UnregisterOverrideConsumer(this); + Logging.Debug("[SkipCinematic] Unregistered as input override consumer"); + } } private void HandleCinematicStarted() { - InputManager.Instance.RegisterOverrideConsumer(this); + if (InputManager.Instance != null) + { + InputManager.Instance.RegisterOverrideConsumer(this); + } } private void HandleCinematicStopped() { - InputManager.Instance.UnregisterOverrideConsumer(this); + if (InputManager.Instance != null) + { + InputManager.Instance.UnregisterOverrideConsumer(this); + } } void Update() diff --git a/Assets/Scripts/Core/SceneManagerService.cs b/Assets/Scripts/Core/SceneManagerService.cs index 2df3ca5f..c97ceda9 100644 --- a/Assets/Scripts/Core/SceneManagerService.cs +++ b/Assets/Scripts/Core/SceneManagerService.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using UI; using UnityEngine; using UnityEngine.SceneManagement; +using Bootstrap; namespace Core { @@ -48,30 +49,17 @@ namespace Core private readonly Dictionary _activeUnloads = new(); private const string BootstrapSceneName = "BootstrapScene"; - void Start() - { - _loadingScreen = LoadingScreenController.Instance; - - // Set up loading screen event handlers - SetupLoadingScreenEvents(); - } - - void Awake() { _instance = this; // DontDestroyOnLoad(gameObject); -#if UNITY_EDITOR - // In Editor, set CurrentGameplayScene to the currently open scene at play start - if (Application.isPlaying) - { - var activeScene = SceneManager.GetActiveScene(); - if (activeScene.IsValid()) - { - CurrentGameplayScene = activeScene.name; - } - } -#endif + + // Initialize current scene tracking immediately in Awake + InitializeCurrentSceneTracking(); + + // Register for post-boot initialization + BootCompletionService.RegisterInitAction(InitializePostBoot); + // Ensure BootstrapScene is loaded at startup var bootstrap = SceneManager.GetSceneByName(BootstrapSceneName); if (!bootstrap.isLoaded) @@ -79,6 +67,53 @@ namespace Core SceneManager.LoadScene(BootstrapSceneName, LoadSceneMode.Additive); } } + + /// + /// Initialize current scene tracking immediately in Awake + /// This ensures scene management works correctly regardless of boot timing + /// + private void InitializeCurrentSceneTracking() + { + // Get the active scene and use it as the current gameplay scene + Scene activeScene = SceneManager.GetActiveScene(); + + if (activeScene.IsValid()) + { + // If this is the MainMenu or another gameplay scene, track it + if (activeScene.name != BootstrapSceneName) + { + CurrentGameplayScene = activeScene.name; + Logging.Debug($"[SceneManagerService] Initialized with current scene: {CurrentGameplayScene}"); + } + // Otherwise default to MainMenu + else + { + CurrentGameplayScene = "MainMenu"; + Logging.Debug($"[SceneManagerService] Initialized with default scene: {CurrentGameplayScene}"); + } + } + else + { + CurrentGameplayScene = "MainMenu"; + Logging.Debug($"[SceneManagerService] No valid active scene, defaulting to: {CurrentGameplayScene}"); + } + } + + void Start() + { + // LoadingScreen setup moved to InitializePostBoot + } + + private void InitializePostBoot() + { + // Set up loading screen reference and events after boot is complete + _loadingScreen = LoadingScreenController.Instance; + + // Set up loading screen event handlers if available + SetupLoadingScreenEvents(); + + Logging.Debug($"[SceneManagerService] Post-boot initialization complete, current scene is: {CurrentGameplayScene}"); + } private void SetupLoadingScreenEvents() { diff --git a/Assets/Scripts/Input/InputManager.cs b/Assets/Scripts/Input/InputManager.cs index a095e18f..2831557e 100644 --- a/Assets/Scripts/Input/InputManager.cs +++ b/Assets/Scripts/Input/InputManager.cs @@ -5,6 +5,7 @@ using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; using AppleHills.Core.Settings; +using Bootstrap; using Core; // Added for IInteractionSettings namespace Input @@ -78,13 +79,30 @@ namespace Input tapMoveAction = playerInput.actions.FindAction("TapMove", false); holdMoveAction = playerInput.actions.FindAction("HoldMove", false); positionAction = playerInput.actions.FindAction("TouchPosition", false); + + // Register for post-boot initialization + BootCompletionService.RegisterInitAction(InitializePostBoot); } private void Start() { - // SceneManagerService.Instance.SceneLoadCompleted += SwitchInputOnSceneLoaded; + // SceneManagerService subscription moved to InitializePostBoot SwitchInputOnSceneLoaded(SceneManager.GetActiveScene().name); } + + private void InitializePostBoot() + { + // Subscribe to scene load completed events now that boot is complete + SceneManagerService.Instance.SceneLoadCompleted += SwitchInputOnSceneLoaded; + Logging.Debug("[InputManager] Subscribed to SceneManagerService events"); + } + + private void OnDestroy() + { + // Unsubscribe from SceneManagerService + if (SceneManagerService.Instance != null) + SceneManagerService.Instance.SceneLoadCompleted -= SwitchInputOnSceneLoaded; + } private void SwitchInputOnSceneLoaded(string sceneName) { diff --git a/Assets/Scripts/PuzzleS/PuzzleManager.cs b/Assets/Scripts/PuzzleS/PuzzleManager.cs index 514a1cfd..91a87461 100644 --- a/Assets/Scripts/PuzzleS/PuzzleManager.cs +++ b/Assets/Scripts/PuzzleS/PuzzleManager.cs @@ -5,6 +5,7 @@ using System.Linq; using UnityEngine; using UnityEngine.SceneManagement; using AppleHills.Core.Settings; +using Bootstrap; using Core; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; @@ -73,6 +74,9 @@ namespace PuzzleS // Initialize settings reference _interactionSettings = GameManager.GetSettingsObject(); + + // Register for post-boot initialization + BootCompletionService.RegisterInitAction(InitializePostBoot); } void OnEnable() @@ -83,7 +87,7 @@ namespace PuzzleS void Start() { - SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoadCompleted; + // SceneManagerService subscription moved to InitializePostBoot // Find player transform _playerTransform = GameObject.FindGameObjectWithTag("Player")?.transform; @@ -97,7 +101,14 @@ namespace PuzzleS LoadPuzzleDataForCurrentScene(); } } - + + private void InitializePostBoot() + { + // Subscribe to SceneManagerService events after boot is complete + SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoadCompleted; + Logging.Debug("[PuzzleManager] Subscribed to SceneManagerService events"); + } + void OnDestroy() { StopProximityChecks();