First steps
This commit is contained in:
31
Assets/Scripts/PuzzleS/IPuzzlePrompt.cs
Normal file
31
Assets/Scripts/PuzzleS/IPuzzlePrompt.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace PuzzleS
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for proximity-based puzzle prompts that can show far/close indicators
|
||||
/// based on player distance.
|
||||
/// </summary>
|
||||
public interface IPuzzlePrompt
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when the player enters the outer range of the prompt.
|
||||
/// </summary>
|
||||
void ShowFar();
|
||||
|
||||
/// <summary>
|
||||
/// Called when the player enters the inner range of the prompt.
|
||||
/// </summary>
|
||||
void ShowClose();
|
||||
|
||||
/// <summary>
|
||||
/// Called when the player exits the inner range of the prompt.
|
||||
/// </summary>
|
||||
void HideClose();
|
||||
|
||||
/// <summary>
|
||||
/// Called when the player exits the outer range of the prompt.
|
||||
/// </summary>
|
||||
void HideFar();
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/PuzzleS/IPuzzlePrompt.cs.meta
Normal file
3
Assets/Scripts/PuzzleS/IPuzzlePrompt.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84466e5dfeeb4908adf7f0f1f2b82531
|
||||
timeCreated: 1759312472
|
||||
@@ -1,6 +1,8 @@
|
||||
using Input;
|
||||
using Interactions;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using AppleHills.Core.Settings;
|
||||
|
||||
namespace PuzzleS
|
||||
{
|
||||
@@ -8,14 +10,27 @@ namespace PuzzleS
|
||||
/// Manages the state and interactions for a single puzzle step, including unlock/lock logic and event handling.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Interactable))]
|
||||
public class ObjectiveStepBehaviour : MonoBehaviour
|
||||
public class ObjectiveStepBehaviour : MonoBehaviour, IPuzzlePrompt
|
||||
{
|
||||
/// <summary>
|
||||
/// The data object representing this puzzle step.
|
||||
/// </summary>
|
||||
public PuzzleStepSO stepData;
|
||||
|
||||
[Header("Indicator Settings")]
|
||||
[SerializeField] private GameObject indicatorPrefab;
|
||||
[SerializeField] private bool drawPromptRangeGizmo = true;
|
||||
|
||||
private Interactable _interactable;
|
||||
private bool _isUnlocked = false;
|
||||
private GameObject _spawnedIndicator;
|
||||
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()
|
||||
{
|
||||
@@ -47,6 +62,103 @@ namespace PuzzleS
|
||||
_interactable.interactionComplete.RemoveListener(OnInteractionComplete);
|
||||
}
|
||||
PuzzleManager.Instance?.UnregisterStepBehaviour(this);
|
||||
|
||||
// Clean up indicator
|
||||
if (_spawnedIndicator != null)
|
||||
{
|
||||
Destroy(_spawnedIndicator);
|
||||
_spawnedIndicator = null;
|
||||
_indicator = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the proximity state from PuzzleManager and triggers appropriate methods.
|
||||
/// </summary>
|
||||
/// <param name="newState">The new proximity state.</param>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Called when player enters the far range
|
||||
/// </summary>
|
||||
public virtual void ShowFar()
|
||||
{
|
||||
// Delegate to indicator if available
|
||||
if (_indicator != null)
|
||||
{
|
||||
_indicator.ShowFar();
|
||||
return;
|
||||
}
|
||||
|
||||
// Default fallback behavior
|
||||
Debug.Log($"[Puzzles] Player entered far range of {stepData?.stepId} on {gameObject.name}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when player enters the close range
|
||||
/// </summary>
|
||||
public virtual void ShowClose()
|
||||
{
|
||||
// Delegate to indicator if available
|
||||
if (_indicator != null)
|
||||
{
|
||||
_indicator.ShowClose();
|
||||
return;
|
||||
}
|
||||
|
||||
// Default fallback behavior
|
||||
Debug.Log($"[Puzzles] Player entered close range of {stepData?.stepId} on {gameObject.name}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when player exits the close range
|
||||
/// </summary>
|
||||
public virtual void HideClose()
|
||||
{
|
||||
// Delegate to indicator if available
|
||||
if (_indicator != null)
|
||||
{
|
||||
_indicator.HideClose();
|
||||
return;
|
||||
}
|
||||
|
||||
// Default fallback behavior
|
||||
Debug.Log($"[Puzzles] Player exited close range of {stepData?.stepId} on {gameObject.name}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when player exits the far range
|
||||
/// </summary>
|
||||
public virtual void HideFar()
|
||||
{
|
||||
// Delegate to indicator if available
|
||||
if (_indicator != null)
|
||||
{
|
||||
_indicator.HideFar();
|
||||
return;
|
||||
}
|
||||
|
||||
// Default fallback behavior
|
||||
Debug.Log($"[Puzzles] Player exited far range of {stepData?.stepId} on {gameObject.name}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -56,7 +168,54 @@ namespace PuzzleS
|
||||
{
|
||||
_isUnlocked = true;
|
||||
Debug.Log($"[Puzzles] Step unlocked: {stepData?.stepId} on {gameObject.name}");
|
||||
// Optionally, show visual feedback for unlocked state
|
||||
|
||||
// Show indicator if enabled in settings
|
||||
if (stepData?.ShowIndicator == true && indicatorPrefab != null && _spawnedIndicator == null)
|
||||
{
|
||||
_spawnedIndicator = Instantiate(indicatorPrefab, transform);
|
||||
// Try to get the IPuzzlePrompt component from the spawned indicator
|
||||
_indicator = _spawnedIndicator.GetComponent<IPuzzlePrompt>();
|
||||
|
||||
if (_indicator == null)
|
||||
{
|
||||
// Try to find it in children if not on the root
|
||||
_indicator = _spawnedIndicator.GetComponentInChildren<IPuzzlePrompt>();
|
||||
}
|
||||
|
||||
if (_indicator == null)
|
||||
{
|
||||
Debug.LogWarning($"[Puzzles] Indicator prefab for {stepData?.stepId} does not implement IPuzzlePrompt");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Immediately 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -66,7 +225,14 @@ namespace PuzzleS
|
||||
{
|
||||
_isUnlocked = false;
|
||||
Debug.Log($"[Puzzles] Step locked: {stepData?.stepId} on {gameObject.name}");
|
||||
// Optionally, show visual feedback for locked state
|
||||
|
||||
// Hide indicator
|
||||
if (_spawnedIndicator != null)
|
||||
{
|
||||
Destroy(_spawnedIndicator);
|
||||
_spawnedIndicator = null;
|
||||
_indicator = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -98,5 +264,20 @@ namespace PuzzleS
|
||||
PuzzleManager.Instance?.MarkPuzzleStepCompleted(stepData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visualizes the puzzle prompt ranges in the editor.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
@@ -14,6 +15,12 @@ namespace PuzzleS
|
||||
private static PuzzleManager _instance;
|
||||
private static bool _isQuitting;
|
||||
|
||||
[SerializeField] private float proximityCheckInterval = 0.02f;
|
||||
|
||||
// Reference to player transform for proximity calculations
|
||||
private Transform _playerTransform;
|
||||
private Coroutine _proximityCheckCoroutine;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton instance of the PuzzleManager.
|
||||
/// </summary>
|
||||
@@ -53,9 +60,19 @@ namespace PuzzleS
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
// Find player transform
|
||||
_playerTransform = GameObject.FindGameObjectWithTag("Player")?.transform;
|
||||
|
||||
// Start proximity check coroutine
|
||||
StartProximityChecks();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
StopProximityChecks();
|
||||
}
|
||||
|
||||
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
@@ -66,6 +83,69 @@ namespace PuzzleS
|
||||
_runtimeDependencies.Clear();
|
||||
BuildRuntimeDependencies();
|
||||
UnlockInitialSteps();
|
||||
|
||||
// Find player transform again in case it changed with scene load
|
||||
_playerTransform = GameObject.FindGameObjectWithTag("Player")?.transform;
|
||||
|
||||
// Restart proximity checks
|
||||
StartProximityChecks();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the proximity check coroutine.
|
||||
/// </summary>
|
||||
private void StartProximityChecks()
|
||||
{
|
||||
StopProximityChecks();
|
||||
_proximityCheckCoroutine = StartCoroutine(CheckProximityRoutine());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the proximity check coroutine.
|
||||
/// </summary>
|
||||
private void StopProximityChecks()
|
||||
{
|
||||
if (_proximityCheckCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_proximityCheckCoroutine);
|
||||
_proximityCheckCoroutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that periodically checks player proximity to all puzzle steps.
|
||||
/// </summary>
|
||||
private IEnumerator CheckProximityRoutine()
|
||||
{
|
||||
WaitForSeconds wait = new WaitForSeconds(proximityCheckInterval);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (_playerTransform != null)
|
||||
{
|
||||
// Get the proximity threshold from settings (half of the prompt range)
|
||||
float proximityThreshold = GameManager.Instance.DefaultPuzzlePromptRange;
|
||||
|
||||
// Check distance to each step behavior
|
||||
foreach (var kvp in _stepBehaviours)
|
||||
{
|
||||
if (kvp.Value == null) continue;
|
||||
|
||||
float distance = Vector3.Distance(_playerTransform.position, kvp.Value.transform.position);
|
||||
|
||||
// Determine the proximity state - only Close or Far now
|
||||
ObjectiveStepBehaviour.ProximityState state =
|
||||
(distance <= proximityThreshold)
|
||||
? ObjectiveStepBehaviour.ProximityState.Close
|
||||
: ObjectiveStepBehaviour.ProximityState.Far;
|
||||
|
||||
// Update the step's proximity state
|
||||
kvp.Value.UpdateProximityState(state);
|
||||
}
|
||||
}
|
||||
|
||||
yield return wait;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -29,4 +29,19 @@ public class PuzzleStepSO : ScriptableObject
|
||||
/// </summary>
|
||||
[Header("Unlocks")]
|
||||
public List<PuzzleStepSO> unlocks = new List<PuzzleStepSO>();
|
||||
|
||||
[Header("Interaction Settings")]
|
||||
[Tooltip("Whether to show an indicator when this step is unlocked")]
|
||||
[SerializeField] private bool showIndicator = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to show an indicator when this step is unlocked.
|
||||
/// </summary>
|
||||
public bool ShowIndicator => showIndicator;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to show an indicator.
|
||||
/// </summary>
|
||||
public bool GetShowIndicator() => showIndicator;
|
||||
public void SetShowIndicator(bool value) => showIndicator = value;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user