Hallucination system

This commit is contained in:
Ferlat Thibaud
2026-03-16 17:06:47 +01:00
parent 10faf74405
commit 0d968f75ab
44 changed files with 2860 additions and 909 deletions
@@ -53,8 +53,35 @@ namespace Unheimlich.Features.Dialogue
private float _autoCloseTimer = 0f;
private bool _isWaitingForAutoClose = false;
protected override void OnAwake()
protected override void OnAwake() { }
protected override void OnEnabled()
{
// With Scene Reload disabled, Start() doesn't run again each session.
// We recreate the focus system to clear any stale locked state (_isFocusLocked,
// _focusTimer, _currentFocusedHandler) that may have carried over.
if (_focusSystem != null)
{
_mainCamera = Camera.main;
_focusSystem = new DialogueFocusSystem(
this,
_mainCamera,
PlayerController.Instance?.CameraEffects,
_choiceLayer,
_focusRange,
_focusTime,
_raycastInterval,
_recoveryDuration,
_focusRetentionTime,
_fallbackShakeIntensity
);
_focusSystem.OnChoiceMade += (choice) => OnChoiceMade?.Invoke(choice);
// Also reset dialogue state in case we exited mid-dialogue
IsInDialogue = false;
_isWaitingForAutoClose = false;
_autoCloseTimer = 0f;
}
}
private void Start()
@@ -82,6 +109,13 @@ namespace Unheimlich.Features.Dialogue
if (!IsInDialogue)
return;
if (_activeDialogueUI == null)
{
Debug.LogError("[DialogueManager] IsInDialogue is true but _activeDialogueUI is null. Forcing dialogue end.", this);
IsInDialogue = false;
return;
}
if (!_activeDialogueUI.AreChoicesVisible())
{
if (_isWaitingForAutoClose && _currentNode != null && _currentNode.AutoCloseDelay > 0f)
@@ -142,6 +176,12 @@ namespace Unheimlich.Features.Dialogue
_activeDialogueUI = customDialogueUI;
CurrentOwner = owner;
if (_activeDialogueUI == null)
{
Debug.LogError("[DialogueManager] StartConversation called with no DialogueUI. Assign a Local Dialogue UI on the DialogueTriggerZone that triggered this conversation.", this);
return;
}
ShowCurrentNode();
}
@@ -2,6 +2,7 @@ using UnityEngine;
using Unheimlich.Features.Dialogue.Data;
using Unheimlich.Features.Dialogue;
using Unheimlich.Features.Dialogue.UI;
using Unheimlich.Core.Events;
namespace Unheimlich.Features.Dialogue
{
@@ -9,9 +10,8 @@ namespace Unheimlich.Features.Dialogue
/// Trigger zone that starts a dialogue sequence when player enters.
/// Supports pause/resume on exit/enter and optional one-time triggering.
/// </summary>
[RequireComponent(typeof(Collider))]
public class DialogueTriggerZone : MonoBehaviour
{
public class DialogueTriggerZone : TriggerZoneBase
{
[Header("Dialogue Reference")]
[Tooltip("DialogueConversation to use for this trigger zone")]
[SerializeField] private DialogueConversation _conversation;
@@ -19,8 +19,8 @@ namespace Unheimlich.Features.Dialogue
[Header("Trigger Settings")]
[Tooltip("Can the dialogue be retriggered after completion")]
[SerializeField] private bool _isRetriggerable = false;
[SerializeField] private bool _requirePlayerTag = true;
[SerializeField] private string _playerTag = "Player";
protected override bool IsRetriggerable => _isRetriggerable;
[Header("Distance Check")]
[Tooltip("Pause dialogue if player exits the trigger zone")]
@@ -41,29 +41,19 @@ namespace Unheimlich.Features.Dialogue
[SerializeField] private bool _enableBlurEffect = true;
private bool _hasTriggered = false;
private Collider _triggerCollider;
private DialogueStateHandler _dialogueState;
private void Awake()
protected override void Awake()
{
_triggerCollider = GetComponent<Collider>();
base.Awake();
_dialogueState = new DialogueStateHandler(this);
if (!_triggerCollider.isTrigger)
{
Debug.LogWarning($"{gameObject.name}: Collider should be marked as Trigger!", this);
_triggerCollider.isTrigger = true;
}
if (_floatingWordAnchor == null)
_floatingWordAnchor = transform;
}
private void OnTriggerEnter(Collider other)
protected override void OnPlayerEntered(Collider playerCollider)
{
if (_requirePlayerTag && !other.CompareTag(_playerTag))
return;
if (_dialogueState.IsCompleted && !_isRetriggerable)
return;
@@ -80,11 +70,8 @@ namespace Unheimlich.Features.Dialogue
}
}
private void OnTriggerExit(Collider other)
protected override void OnPlayerExited(Collider playerCollider)
{
if (_requirePlayerTag && !other.CompareTag(_playerTag))
return;
if (_pauseOnExit && DialogueManager.Instance != null && DialogueManager.Instance.IsDialogueActive)
{
if ((Object)DialogueManager.Instance.CurrentOwner == this)
@@ -153,6 +140,7 @@ namespace Unheimlich.Features.Dialogue
{
_hasTriggered = false;
_dialogueState.Reset();
ResetZone();
}
private void OnDestroy()
@@ -161,20 +149,14 @@ namespace Unheimlich.Features.Dialogue
DialogueManager.Instance.OnDialogueCompleted -= OnDialogueCompleted;
}
private void OnValidate()
{
if (_localDialogueUI == null)
Debug.LogWarning($"[DialogueTriggerZone] '{gameObject.name}' has no Local Dialogue UI assigned. Assign the appropriate DialogueUI prefab variant (World, NPC, Room, Screen, etc.).", this);
}
private void OnDrawGizmosSelected()
{
Collider col = GetComponent<Collider>();
if (col != null)
{
Gizmos.color = _hasTriggered ? Color.gray : Color.yellow;
Gizmos.matrix = transform.localToWorldMatrix;
if (col is BoxCollider box)
Gizmos.DrawWireCube(box.center, box.size);
else if (col is SphereCollider sphere)
Gizmos.DrawWireSphere(sphere.center, sphere.radius);
}
if (_floatingWordAnchor != null)
{
Gizmos.color = Color.cyan;
@@ -253,7 +253,8 @@ namespace Unheimlich.Features.Dialogue.UI
{
if (!_positionCaptured)
{
_startLocalPos = transform.localPosition;
// Subtract shake offset to get the clean base position, not the shaken one
_startLocalPos = transform.localPosition - _shakeOffset;
_positionCaptured = true;
LeanTween.cancel(gameObject);
@@ -516,13 +517,15 @@ namespace Unheimlich.Features.Dialogue.UI
ResetSketchFade();
// Reset position capture and restart shake immediately — don't rely on a LeanTween
// callback for this, since stale tween IDs (Domain Reload disabled) can silently
// drop callbacks, leaving some words permanently non-shaking.
_positionCaptured = false;
StartOrganicShake();
TweenHelper.MoveLocal(gameObject, _startLocalPos, duration, () => {
_positionCaptured = false;
if (DialogueManager.Instance != null)
DialogueManager.Instance.UnlockFocus();
StartOrganicShake();
}, LeanTweenType.easeOutQuad);
transform.localScale = Vector3.one * _originalScale;