216 lines
7.4 KiB
C#
216 lines
7.4 KiB
C#
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<Collider>();
|
|
if (triggerCollider != null) triggerCollider.isTrigger = true;
|
|
|
|
if (audioSource == null) audioSource = GetComponent<AudioSource>();
|
|
if (audioSource == null && destinationSound != null) audioSource = gameObject.AddComponent<AudioSource>();
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
} |