321 lines
8.5 KiB
C#
321 lines
8.5 KiB
C#
using System.Collections;
|
|
using UnityEngine;
|
|
|
|
public class EyeUnlockSequence : MonoBehaviour
|
|
{
|
|
[SerializeField] private GameObject[] segmentSequence;
|
|
[SerializeField] private float[] timeWindows;
|
|
|
|
[Header("Initial Focus Requirement")]
|
|
[SerializeField] private float initialFocusTime = 1f;
|
|
[SerializeField] private GameObject focusIndicator;
|
|
|
|
[Header("Visual Feedback")]
|
|
[SerializeField] private Material completedMaterial;
|
|
[SerializeField] private Material defaultMaterial;
|
|
[SerializeField] private Material focusingMaterial;
|
|
[SerializeField] private Material greenMaterial;
|
|
[SerializeField] private Material redMaterial;
|
|
|
|
[Header("Color State")]
|
|
[SerializeField] private bool isGreen = false;
|
|
|
|
[Header("Light Activation")]
|
|
[SerializeField] private Light pointLightToActivate;
|
|
[SerializeField] private bool startLightEnabled = false;
|
|
|
|
[Header("Reset Parameters")]
|
|
[SerializeField] private float resetHoldDuration = 1f;
|
|
[SerializeField] private float resetCooldownTime = 1f;
|
|
|
|
[Header("Manager Reference")]
|
|
[SerializeField] private SequenceManagerLightTurnOn sequenceManager;
|
|
|
|
private int currentStep = -1;
|
|
private Coroutine sequenceCoroutine;
|
|
private Coroutine focusCoroutine;
|
|
private EyeInteractable eyeInteractable;
|
|
private MeshRenderer meshRenderer;
|
|
private bool isSequenceCompleted = false;
|
|
private Material originalMaterial;
|
|
private float hoverStartTime;
|
|
private bool isResettingSequence = false;
|
|
private float lastHoverEndTime;
|
|
private bool isFocusTimeMet = false;
|
|
private bool isCurrentlyFocusing = false;
|
|
private float currentFocusTime = 0f;
|
|
|
|
private void Start()
|
|
{
|
|
eyeInteractable = GetComponent<EyeInteractable>();
|
|
meshRenderer = GetComponent<MeshRenderer>();
|
|
|
|
if (meshRenderer != null)
|
|
{
|
|
originalMaterial = meshRenderer.material;
|
|
UpdateMaterialBasedOnState(); // Respect starting green/red state
|
|
}
|
|
|
|
DeactivateAllSegments();
|
|
|
|
if (pointLightToActivate != null)
|
|
pointLightToActivate.enabled = startLightEnabled;
|
|
|
|
if (focusIndicator != null)
|
|
focusIndicator.SetActive(false);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (eyeInteractable != null)
|
|
{
|
|
if (eyeInteractable.IsHovered)
|
|
{
|
|
if (!isFocusTimeMet && !isCurrentlyFocusing)
|
|
{
|
|
StartFocusTimer();
|
|
}
|
|
|
|
if (isFocusTimeMet)
|
|
{
|
|
if (currentStep == -1 && !isSequenceCompleted)
|
|
{
|
|
Debug.Log("▶ Starting sequence...");
|
|
currentStep = 0;
|
|
StartNextSegment();
|
|
}
|
|
|
|
if (isSequenceCompleted)
|
|
{
|
|
HandleSequenceReset();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (isCurrentlyFocusing)
|
|
{
|
|
CancelFocusTimer();
|
|
}
|
|
|
|
if (isResettingSequence)
|
|
{
|
|
isResettingSequence = false;
|
|
Debug.Log("Reset attempt cancelled.");
|
|
}
|
|
|
|
hoverStartTime = 0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void StartFocusTimer()
|
|
{
|
|
isCurrentlyFocusing = true;
|
|
currentFocusTime = 0f;
|
|
|
|
if (focusIndicator != null)
|
|
focusIndicator.SetActive(true);
|
|
|
|
if (meshRenderer != null && focusingMaterial != null)
|
|
meshRenderer.material = focusingMaterial;
|
|
|
|
if (focusCoroutine != null)
|
|
StopCoroutine(focusCoroutine);
|
|
|
|
focusCoroutine = StartCoroutine(FocusTimerCoroutine());
|
|
}
|
|
|
|
private IEnumerator FocusTimerCoroutine()
|
|
{
|
|
while (currentFocusTime < initialFocusTime)
|
|
{
|
|
if (!eyeInteractable.IsHovered)
|
|
{
|
|
CancelFocusTimer();
|
|
yield break;
|
|
}
|
|
|
|
currentFocusTime += Time.deltaTime;
|
|
yield return null;
|
|
}
|
|
|
|
isFocusTimeMet = true;
|
|
isCurrentlyFocusing = false;
|
|
|
|
if (focusIndicator != null)
|
|
focusIndicator.SetActive(false);
|
|
|
|
UpdateMaterialBasedOnState();
|
|
focusCoroutine = null;
|
|
}
|
|
|
|
private void CancelFocusTimer()
|
|
{
|
|
isCurrentlyFocusing = false;
|
|
currentFocusTime = 0f;
|
|
|
|
if (focusCoroutine != null)
|
|
{
|
|
StopCoroutine(focusCoroutine);
|
|
focusCoroutine = null;
|
|
}
|
|
|
|
if (focusIndicator != null)
|
|
focusIndicator.SetActive(false);
|
|
|
|
UpdateMaterialBasedOnState();
|
|
}
|
|
|
|
private void HandleSequenceReset()
|
|
{
|
|
if (Time.time - lastHoverEndTime < resetCooldownTime)
|
|
return;
|
|
|
|
if (!isResettingSequence)
|
|
{
|
|
hoverStartTime = Time.time;
|
|
isResettingSequence = true;
|
|
}
|
|
|
|
if (Time.time - hoverStartTime >= resetHoldDuration)
|
|
{
|
|
ResetSequenceCompletely();
|
|
currentStep = 0;
|
|
StartNextSegment();
|
|
isResettingSequence = false;
|
|
}
|
|
}
|
|
|
|
private void StartNextSegment()
|
|
{
|
|
if (isSequenceCompleted) return;
|
|
if (currentStep >= segmentSequence.Length)
|
|
{
|
|
HandleSequenceCompleted();
|
|
return;
|
|
}
|
|
|
|
GameObject segment = segmentSequence[currentStep];
|
|
float timeout = (timeWindows.Length > currentStep) ? timeWindows[currentStep] : 3f;
|
|
|
|
segment.SetActive(true); // show this segment
|
|
|
|
if (sequenceCoroutine != null)
|
|
StopCoroutine(sequenceCoroutine);
|
|
|
|
sequenceCoroutine = StartCoroutine(HandleSegmentWithTimeout(segment, timeout));
|
|
}
|
|
|
|
private IEnumerator HandleSegmentWithTimeout(GameObject segment, float timeLimit)
|
|
{
|
|
if (isSequenceCompleted) yield break;
|
|
|
|
EyeInteractable segmentInteractable = segment.GetComponent<EyeInteractable>();
|
|
if (segmentInteractable == null)
|
|
{
|
|
Debug.LogWarning($"{segment.name} missing EyeInteractable.");
|
|
yield break;
|
|
}
|
|
|
|
float timer = 0f;
|
|
bool success = false;
|
|
|
|
while (timer < timeLimit)
|
|
{
|
|
if (segmentInteractable.IsHovered)
|
|
{
|
|
success = true;
|
|
break;
|
|
}
|
|
|
|
timer += Time.deltaTime;
|
|
yield return null;
|
|
}
|
|
|
|
// ✅ We do NOT deactivate the segment here anymore!
|
|
// This allows previous segments to remain visible.
|
|
|
|
if (success)
|
|
{
|
|
currentStep++;
|
|
StartNextSegment();
|
|
}
|
|
else
|
|
{
|
|
ResetSequence();
|
|
}
|
|
}
|
|
|
|
private void HandleSequenceCompleted()
|
|
{
|
|
isSequenceCompleted = true;
|
|
lastHoverEndTime = Time.time;
|
|
|
|
if (sequenceCoroutine != null)
|
|
StopCoroutine(sequenceCoroutine);
|
|
|
|
DeactivateAllSegments(); // optional — if you want to hide them after success
|
|
|
|
// Toggle state and notify manager
|
|
IsGreen = !IsGreen;
|
|
|
|
if (sequenceManager != null)
|
|
sequenceManager.NotifyCompleted(this.gameObject);
|
|
|
|
isFocusTimeMet = false;
|
|
}
|
|
|
|
public void UpdateMaterialBasedOnState()
|
|
{
|
|
if (meshRenderer == null) return;
|
|
|
|
if (isGreen && greenMaterial != null)
|
|
meshRenderer.material = greenMaterial;
|
|
else if (!isGreen && redMaterial != null)
|
|
meshRenderer.material = redMaterial;
|
|
else if (defaultMaterial != null)
|
|
meshRenderer.material = defaultMaterial;
|
|
else
|
|
meshRenderer.material = originalMaterial;
|
|
}
|
|
|
|
public bool IsGreen
|
|
{
|
|
get => isGreen;
|
|
set
|
|
{
|
|
isGreen = value;
|
|
UpdateMaterialBasedOnState();
|
|
}
|
|
}
|
|
|
|
public void SetMaterials(Material green, Material red)
|
|
{
|
|
if (green != null) greenMaterial = green;
|
|
if (red != null) redMaterial = red;
|
|
}
|
|
|
|
private void ResetSequence()
|
|
{
|
|
if (isSequenceCompleted) return;
|
|
|
|
DeactivateAllSegments();
|
|
currentStep = -1;
|
|
isFocusTimeMet = false;
|
|
}
|
|
|
|
private void ResetSequenceCompletely()
|
|
{
|
|
isSequenceCompleted = false;
|
|
ResetSequence();
|
|
}
|
|
|
|
private void DeactivateAllSegments()
|
|
{
|
|
foreach (var segment in segmentSequence)
|
|
if (segment != null)
|
|
segment.SetActive(false);
|
|
}
|
|
}
|