using System.Collections; using UnityEngine; using UnityEngine.Events; namespace Beyond { [RequireComponent(typeof(Collider))] public class DestinationPoint : MonoBehaviour { [Header("References")] [SerializeField] private DestinationPoint nextPoint; // Następny punkt [SerializeField] private Collider triggerCollider; [SerializeField] private AudioSource audioSource; [SerializeField] private AudioClip destinationSound; [Header("Events")] [SerializeField] private UnityEvent onPointReached; [Header("Detection Settings")] [SerializeField] private LayerMask playerLayer; // Lepsze niż Tag [Tooltip("Jeśli true: zalicza punkt natychmiast, jeśli gracz już w nim stoi w momencie aktywacji.")] [SerializeField] private bool autoSkipIfInside = false; [Tooltip("Jeśli true: punkt nie włączy się, dopóki gracz nie oddali się na bezpieczną odległość.")] [SerializeField] private bool waitForExitIfInside = true; [SerializeField] private float minActivationDistance = 5.0f; // Dystans wymagany do uzbrojenia punktu [Header("General Settings")] [SerializeField] private bool activateOnStart = true; private bool isActive = false; // Czy punkt logicznie jest "tym aktualnym" private bool isArmed = false; // Czy punkt jest fizycznie włączony (collider + marker) private Transform playerTransform; private Coroutine activationCoroutine; private void Awake() { if (triggerCollider == null) triggerCollider = GetComponent(); if (triggerCollider != null) triggerCollider.isTrigger = true; if (audioSource == null) audioSource = GetComponent(); if (audioSource == null && destinationSound != null) audioSource = gameObject.AddComponent(); } private void Start() { GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); if (playerObj) playerTransform = playerObj.transform; if (activateOnStart) { Activate(); } else { // Upewnij się, że na starcie jest wyłączony, jeśli nie ma być aktywny UpdateVisualState(false); } } // --- Logika Włączania/Wyłączania (Unity Lifecycle) --- private void OnEnable() { // Jeśli obiekt został włączony ponownie (np. po dezaktywacji rodzica), // a logicznie ten punkt nadal powinien być aktywny -> wznów działanie. if (isActive) { TryArmPoint(); } } private void OnDisable() { // Kiedy obiekt jest wyłączany, zawsze chowamy marker i wyłączamy logikę // Ale NIE zmieniamy 'isActive', bo quest nadal jest na tym etapie. if (activationCoroutine != null) StopCoroutine(activationCoroutine); UpdateVisualState(false); } // --- Główna Logika --- public void Activate() { if (isActive) return; // Już jest aktywny isActive = true; TryArmPoint(); } private void TryArmPoint() { // 1. Sprawdź, czy gracz jest w środku if (IsPlayerInsideOrTooClose()) { if (autoSkipIfInside) { Debug.Log($"[DestinationPoint] Gracz wewnątrz {name}, automatyczne zaliczenie."); HandleDestinationReached(); return; } else if (waitForExitIfInside) { Debug.Log($"[DestinationPoint] Gracz za blisko {name}, czekam na dystans..."); if (activationCoroutine != null) StopCoroutine(activationCoroutine); activationCoroutine = StartCoroutine(WaitForSafeDistance()); return; } } // 2. Normalna aktywacja (Marker + Collider) UpdateVisualState(true); } public void Deactivate() { isActive = false; UpdateVisualState(false); if (activationCoroutine != null) StopCoroutine(activationCoroutine); } // Metoda pomocnicza do zarządzania stanem wizualnym/fizycznym private void UpdateVisualState(bool state) { isArmed = state; if (triggerCollider != null) triggerCollider.enabled = state; if (Compass.Instance != null) { if (state) Compass.Instance.AddQuestMarker(transform, null); else Compass.Instance.RemoveQuestMarker(transform); } } private void OnTriggerEnter(Collider other) { if (!isActive || !isArmed) return; // Sprawdzamy LayerMask lub Tag if (IsInLayerMask(other.gameObject, playerLayer) || other.CompareTag("Player")) { HandleDestinationReached(); } } private void HandleDestinationReached() { // Dźwięk if (destinationSound != null && audioSource != null) audioSource.PlayOneShot(destinationSound); // Eventy onPointReached?.Invoke(); // Logika następnego punktu // Najpierw wyłączamy obecny, żeby nie wywołał się dwa razy Deactivate(); if (nextPoint != null) { nextPoint.Activate(); } } // --- Pomocnicze (Dystans i Coroutine) --- private bool IsPlayerInsideOrTooClose() { if (playerTransform == null) return false; // Sprawdzenie 1: Czy jest fizycznie w Triggerze if (triggerCollider != null && triggerCollider.bounds.Contains(playerTransform.position)) return true; // Sprawdzenie 2: Czy jest za blisko (zabezpieczenie jeśli collider jest mały) return Vector3.Distance(transform.position, playerTransform.position) < 2.0f; } private IEnumerator WaitForSafeDistance() { // Upewnij się, że marker i collider są wyłączone podczas czekania UpdateVisualState(false); while (playerTransform != null && Vector3.Distance(transform.position, playerTransform.position) < minActivationDistance) { yield return new WaitForSeconds(0.5f); } // Gracz odszedł wystarczająco daleko -> włącz punkt UpdateVisualState(true); activationCoroutine = null; } private bool IsInLayerMask(GameObject obj, LayerMask layerMask) { return (layerMask.value & (1 << obj.layer)) > 0; } // --- Debug w Edytorze --- private void OnDrawGizmos() { Gizmos.color = isActive ? (isArmed ? Color.green : Color.yellow) : Color.gray; Gizmos.DrawWireSphere(transform.position, 0.5f); if (nextPoint != null) { Gizmos.color = Color.white; Gizmos.DrawLine(transform.position, nextPoint.transform.position); } } } }