using Input; using Interactions; using UnityEngine; using System; using AppleHills.Core.Settings; using Core; using UnityEngine.Serialization; namespace PuzzleS { /// /// Manages the state and interactions for a single puzzle step, including unlock/lock logic and event handling. /// [RequireComponent(typeof(Interactable))] public class ObjectiveStepBehaviour : MonoBehaviour, IPuzzlePrompt { /// /// The data object representing this puzzle step. /// public PuzzleStepSO stepData; [Header("Indicator Settings")] [SerializeField] private GameObject puzzleIndicator; [SerializeField] private bool drawPromptRangeGizmo = true; private Interactable _interactable; private bool _isUnlocked = false; private IPuzzlePrompt _indicator; // Current proximity state tracked by PuzzleManager private ProximityState _currentProximityState = ProximityState.Far; // Enum for tracking proximity state (simplified to just Close and Far) public enum ProximityState { Close, Far } void Awake() { _interactable = GetComponent(); } void OnEnable() { if (_interactable == null) _interactable = GetComponent(); if (_interactable != null) { _interactable.interactionStarted.AddListener(OnInteractionStarted); _interactable.interactionComplete.AddListener(OnInteractionComplete); } PuzzleManager.Instance?.RegisterStepBehaviour(this); // Check if this step was already unlocked if (stepData != null && PuzzleManager.Instance != null && PuzzleManager.Instance.IsStepUnlocked(stepData)) { UnlockStep(); } } void OnDestroy() { if (_interactable != null) { _interactable.interactionStarted.RemoveListener(OnInteractionStarted); _interactable.interactionComplete.RemoveListener(OnInteractionComplete); } PuzzleManager.Instance?.UnregisterStepBehaviour(this); } /// /// Updates the proximity state from PuzzleManager and triggers appropriate methods. /// /// The new proximity state. public void UpdateProximityState(ProximityState newState) { if (_currentProximityState == newState) return; // Determine state changes and call appropriate methods if (newState == ProximityState.Close) { // Transitioning from Far to Close ShowClose(); } else // newState == ProximityState.Far { // Transitioning from Close to Far HideClose(); } _currentProximityState = newState; } // IPuzzlePrompt interface implementation - delegates to indicator if available /// /// Called when the prompt should be initially shown (e.g., when step is unlocked) /// public virtual void OnShow() { // Delegate to indicator if available if (_indicator != null) { _indicator.OnShow(); return; } // Default fallback behavior Logging.Debug($"[Puzzles] Prompt shown for {stepData?.stepId} on {gameObject.name}"); } /// /// Called when the prompt should be hidden (e.g., when step is locked) /// public virtual void OnHide() { // Delegate to indicator if available if (_indicator != null) { _indicator.OnHide(); return; } // Default fallback behavior Logging.Debug($"[Puzzles] Prompt hidden for {stepData?.stepId} on {gameObject.name}"); } /// /// Called when player enters the far range /// public virtual void ShowFar() { // Only show if step is unlocked if (!_isUnlocked) return; // Delegate to indicator if available if (_indicator != null) { _indicator.ShowFar(); return; } // Default fallback behavior Logging.Debug($"[Puzzles] Player entered far range of {stepData?.stepId} on {gameObject.name}"); } /// /// Called when player enters the close range /// public virtual void ShowClose() { // Only show if step is unlocked if (!_isUnlocked) return; // Delegate to indicator if available if (_indicator != null) { _indicator.ShowClose(); return; } // Default fallback behavior Logging.Debug($"[Puzzles] Player entered close range of {stepData?.stepId} on {gameObject.name}"); } /// /// Called when player exits the close range /// public virtual void HideClose() { // Only respond if step is unlocked if (!_isUnlocked) return; // Delegate to indicator if available if (_indicator != null) { _indicator.HideClose(); return; } // Default fallback behavior Logging.Debug($"[Puzzles] Player exited close range of {stepData?.stepId} on {gameObject.name}"); } /// /// Called when player exits the far range /// public virtual void HideFar() { // Only respond if step is unlocked if (!_isUnlocked) return; // Delegate to indicator if available if (_indicator != null) { _indicator.HideFar(); return; } // Default fallback behavior Logging.Debug($"[Puzzles] Player exited far range of {stepData?.stepId} on {gameObject.name}"); } /// /// Unlocks this puzzle step, allowing interaction. /// public void UnlockStep() { _isUnlocked = true; Logging.Debug($"[Puzzles] Step unlocked: {stepData?.stepId} on {gameObject.name}"); // Show indicator if enabled in settings if (puzzleIndicator != null) { // Try to get the IPuzzlePrompt component from the spawned indicator _indicator = puzzleIndicator.GetComponent(); if (_indicator == null) { // Try to find it in children if not on the root _indicator = puzzleIndicator.GetComponentInChildren(); } if (_indicator == null) { Logging.Warning($"[Puzzles] Indicator prefab for {stepData?.stepId} does not implement IPuzzlePrompt"); } else { // First show the indicator _indicator.OnShow(); // Then set the correct state based on current player distance Transform playerTransform = GameObject.FindGameObjectWithTag("Player")?.transform; if (playerTransform != null) { float distance = Vector3.Distance(transform.position, playerTransform.position); float promptRange = AppleHills.SettingsAccess.GetPuzzlePromptRange(); if (distance <= promptRange) { // Player is in close range _currentProximityState = ProximityState.Close; _indicator.ShowClose(); } else { // Player is in far range _currentProximityState = ProximityState.Far; _indicator.ShowFar(); } } else { // Default to far if player not found _currentProximityState = ProximityState.Far; _indicator.ShowFar(); } } } } /// /// Locks this puzzle step, preventing interaction. /// public void LockStep() { _isUnlocked = false; Logging.Debug($"[Puzzles] Step locked: {stepData?.stepId} on {gameObject.name}"); // Hide indicator if (_indicator != null) { _indicator.OnHide(); } } /// /// Returns whether this step is currently unlocked. /// public bool IsStepUnlocked() { return _isUnlocked; } /// /// Handles the start of an interaction (can be used for visual feedback). /// private void OnInteractionStarted(PlayerTouchController playerRef, FollowerController followerRef) { // Optionally handle started interaction (e.g. visual feedback) } /// /// Handles completion of the interaction, notifies PuzzleManager if successful and unlocked. /// /// Whether the interaction was successful. private void OnInteractionComplete(bool success) { if (!_isUnlocked) return; if (success) { Logging.Debug($"[Puzzles] Step interacted: {stepData?.stepId} on {gameObject.name}"); PuzzleManager.Instance?.MarkPuzzleStepCompleted(stepData); } } /// /// Visualizes the puzzle prompt ranges in the editor. /// private void OnDrawGizmos() { if (!drawPromptRangeGizmo) return; // Use the global puzzle prompt range from settings float promptRange = AppleHills.SettingsAccess.GetPuzzlePromptRange(); // Draw threshold circle Gizmos.color = Color.cyan; Gizmos.DrawWireSphere(transform.position, promptRange / 2f); } } }