using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; namespace PuzzleS { /// /// Manages puzzle step registration, dependency management, and step completion for the puzzle system. /// public class PuzzleManager : MonoBehaviour { private static PuzzleManager _instance; private static bool _isQuitting; /// /// Singleton instance of the PuzzleManager. /// public static PuzzleManager Instance { get { if (_instance == null && Application.isPlaying && !_isQuitting) { _instance = FindAnyObjectByType(); if (_instance == null) { var go = new GameObject("PuzzleManager"); _instance = go.AddComponent(); // DontDestroyOnLoad(go); } } return _instance; } } // Events to notify about step lifecycle public event Action OnStepCompleted; public event Action OnStepUnlocked; private HashSet _completedSteps = new HashSet(); private HashSet _unlockedSteps = new HashSet(); // Registration for ObjectiveStepBehaviour private Dictionary _stepBehaviours = new Dictionary(); // Runtime dependency graph private Dictionary> _runtimeDependencies = new Dictionary>(); void Awake() { _instance = this; // DontDestroyOnLoad(gameObject); SceneManager.sceneLoaded += OnSceneLoaded; } void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { BuildRuntimeDependencies(); UnlockInitialSteps(); } /// /// Registers a step behaviour with the manager. /// /// The step behaviour to register. public void RegisterStepBehaviour(ObjectiveStepBehaviour behaviour) { if (behaviour?.stepData == null) return; if (!_stepBehaviours.ContainsKey(behaviour.stepData)) { _stepBehaviours.Add(behaviour.stepData, behaviour); Debug.Log($"[Puzzles] Registered step: {behaviour.stepData.stepId} on {behaviour.gameObject.name}"); } } /// /// Unregisters a step behaviour from the manager. /// /// The step behaviour to unregister. public void UnregisterStepBehaviour(ObjectiveStepBehaviour behaviour) { if (behaviour?.stepData == null) return; _stepBehaviours.Remove(behaviour.stepData); Debug.Log($"[Puzzles] Unregistered step: {behaviour.stepData.stepId} on {behaviour.gameObject.name}"); } /// /// Builds the runtime dependency graph for all registered steps. /// private void BuildRuntimeDependencies() { _runtimeDependencies = PuzzleGraphUtility.BuildDependencyGraph(_stepBehaviours.Keys); foreach (var step in _runtimeDependencies.Keys) { foreach (var dep in _runtimeDependencies[step]) { Debug.Log($"[Puzzles] Step {step.stepId} depends on {dep.stepId}"); } } Debug.Log($"[Puzzles] Runtime dependencies built. Total steps: {_stepBehaviours.Count}"); } /// /// Unlocks all initial steps (those with no dependencies). /// private void UnlockInitialSteps() { var initialSteps = PuzzleGraphUtility.FindInitialSteps(_runtimeDependencies); foreach (var step in initialSteps) { Debug.Log($"[Puzzles] Initial step unlocked: {step.stepId}"); UnlockStep(step); } } /// /// Called when a step is completed. Unlocks dependent steps if their dependencies are met. /// /// The completed step. public void MarkPuzzleStepCompleted(PuzzleStepSO step) { if (_completedSteps.Contains(step)) return; _completedSteps.Add(step); Debug.Log($"[Puzzles] Step completed: {step.stepId}"); // Broadcast completion OnStepCompleted?.Invoke(step); foreach (var unlock in step.unlocks) { if (AreRuntimeDependenciesMet(unlock)) { Debug.Log($"[Puzzles] Unlocking step {unlock.stepId} after completing {step.stepId}"); UnlockStep(unlock); } else { Debug.Log($"[Puzzles] Step {unlock.stepId} not unlocked yet, waiting for other dependencies"); } } CheckPuzzleCompletion(); } /// /// Checks if all dependencies for a step are met. /// /// The step to check. /// True if all dependencies are met, false otherwise. private bool AreRuntimeDependenciesMet(PuzzleStepSO step) { if (!_runtimeDependencies.ContainsKey(step) || _runtimeDependencies[step].Count == 0) return true; foreach (var dep in _runtimeDependencies[step]) { if (!_completedSteps.Contains(dep)) return false; } return true; } /// /// Unlocks a specific step and notifies its behaviour. /// /// The step to unlock. private void UnlockStep(PuzzleStepSO step) { if (_unlockedSteps.Contains(step)) return; _unlockedSteps.Add(step); if (_stepBehaviours.TryGetValue(step, out var behaviour)) { behaviour.UnlockStep(); } Debug.Log($"[Puzzles] Step unlocked: {step.stepId}"); // Broadcast unlock OnStepUnlocked?.Invoke(step); } /// /// Checks if the puzzle is complete (all steps finished). /// private void CheckPuzzleCompletion() { if (_completedSteps.Count == _stepBehaviours.Count) { Debug.Log("[Puzzles] Puzzle complete! All steps finished."); // TODO: Fire puzzle complete event or trigger outcome logic } } /// /// Returns whether a step is already unlocked. /// public bool IsStepUnlocked(PuzzleStepSO step) { BuildRuntimeDependencies(); UnlockInitialSteps(); return _unlockedSteps.Contains(step); } void OnApplicationQuit() { _isQuitting = true; } } }