Smooth 3C & simplify hallucination trigger

This commit is contained in:
Ferlat Thibaud
2026-03-22 18:03:21 +01:00
parent 0d968f75ab
commit 1589a7a543
24 changed files with 1318 additions and 621 deletions
@@ -1,112 +1,23 @@
using UnityEngine;
using Unheimlich.Core.Audio;
namespace Unheimlich.Features.Hallucination
{
/// <summary>
/// Fires a hallucination trigger when a specific AudioSource plays, finishes, or reaches a timestamp.
/// Mirrors the pattern of ClueUnlockOnAudio but targets HallucinationManager.
/// Fires a hallucination effect when a specific AudioSource plays, finishes, or reaches a timestamp.
/// Call OnAudioPlay() from whatever drives the AudioSource to start monitoring.
/// </summary>
[RequireComponent(typeof(AudioSource))]
public class HallucinationOnAudio : MonoBehaviour
public class HallucinationOnAudio : AudioTriggerBase
{
public enum FireCondition
{
OnPlay,
OnFinish,
OnTimeReached
}
[Header("Audio Trigger")]
[Tooltip("When to fire the hallucination effect")]
[SerializeField] private FireCondition _fireCondition = FireCondition.OnPlay;
[Tooltip("Playback time in seconds to reach before firing (only for OnTimeReached)")]
[SerializeField] private float _triggerTime = 1f;
[Header("Hallucination Payload")]
[Tooltip("Amount of sanity to add when this fires (0 = no change)")]
[Tooltip("Amount of insanity to add when this fires (0 = no change)")]
[SerializeField, Range(0f, 1f)] private float _insanityAmount = 0f;
[Tooltip("Optional discrete hallucination event to fire")]
[SerializeField] private HallucinationEvent _hallucinationEvent;
[Header("Behaviour")]
[Tooltip("Prevent firing more than once per Play Mode session")]
[SerializeField] private bool _fireOnlyOnce = true;
[Header("Debug")]
[SerializeField] private bool _debugMode;
private AudioSource _audioSource;
private bool _hasFired;
private bool _isMonitoring;
private void Awake()
protected override void Execute()
{
_audioSource = GetComponent<AudioSource>();
}
private void OnEnable()
{
_hasFired = false;
_isMonitoring = false;
if (_audioSource != null && _audioSource.isPlaying)
StartMonitoring();
}
private void Update()
{
if (!_isMonitoring || (_fireOnlyOnce && _hasFired))
return;
if (_audioSource == null || !_audioSource.isPlaying)
{
if (_fireCondition == FireCondition.OnFinish)
Execute();
_isMonitoring = false;
return;
}
if (_fireCondition == FireCondition.OnTimeReached && _audioSource.time >= _triggerTime)
Execute();
}
/// <summary>
/// Notify this component that the AudioSource has started playing.
/// Wire this to whatever code or event drives the AudioSource.
/// </summary>
public void OnAudioPlay()
{
StartMonitoring();
if (_fireCondition == FireCondition.OnPlay)
Execute();
}
/// <summary>
/// Resets the fired state so this can fire again.
/// </summary>
public void ResetFired()
{
_hasFired = false;
_isMonitoring = false;
}
private void StartMonitoring()
{
_isMonitoring = true;
}
private void Execute()
{
if (_fireOnlyOnce && _hasFired)
return;
_hasFired = true;
_isMonitoring = false;
if (HallucinationManager.Instance == null)
{
Debug.LogError("[HallucinationOnAudio] HallucinationManager not found!", this);
@@ -120,7 +31,7 @@ namespace Unheimlich.Features.Hallucination
HallucinationManager.Instance.TriggerHallucinationEvent(_hallucinationEvent);
if (_debugMode)
Debug.Log($"[HallucinationOnAudio] Fired on '{_fireCondition}'. Sanity now: {HallucinationManager.Instance.SanityLevel:F2}", this);
Debug.Log($"[HallucinationOnAudio] Fired on '{_fireCondition}'. Sanity: {HallucinationManager.Instance.SanityLevel:F2}", this);
}
}
}
@@ -1,19 +1,16 @@
using UnityEngine;
using Unheimlich.Core.Events;
namespace Unheimlich.Features.Hallucination
{
/// <summary>
/// Drives the HallucinationManager when its GameEventTrigger fires.
/// Can increase sanity, fire a discrete HallucinationEvent, or both.
/// Compose a GameEventTrigger on the same GameObject to control the firing condition
/// (OnStart, OnEnable, delay, once-flag, external call via UnityEvent, etc.).
/// Standalone hallucination payload component.
/// Call Trigger() from any external script, UnityEvent, interaction, dialogue callback, etc.
/// No other component required alongside it — compose freely.
/// </summary>
[RequireComponent(typeof(GameEventTrigger))]
public class HallucinationTrigger : MonoBehaviour
{
[Header("Sanity")]
[Tooltip("Amount of sanity to add when this trigger fires (0 = no change)")]
[Tooltip("Amount of insanity to add when this trigger fires (0 = no change)")]
[SerializeField, Range(0f, 1f)] private float _insanityAmount = 0f;
[Header("Hallucination Event")]
@@ -23,22 +20,9 @@ namespace Unheimlich.Features.Hallucination
[Header("Debug")]
[SerializeField] private bool _debugMode;
private GameEventTrigger _eventTrigger;
private void Awake()
{
_eventTrigger = GetComponent<GameEventTrigger>();
_eventTrigger.OnTriggered += Trigger;
}
private void OnDestroy()
{
if (_eventTrigger != null)
_eventTrigger.OnTriggered -= Trigger;
}
/// <summary>
/// Fires the hallucination effect. Can be called directly from external scripts or UnityEvents.
/// Fires the hallucination effect.
/// Wire this to any UnityEvent, interaction callback, dialogue node event, audio trigger, etc.
/// </summary>
public void Trigger()
{
@@ -53,7 +37,7 @@ namespace Unheimlich.Features.Hallucination
HallucinationManager.Instance.AddInsanity(_insanityAmount);
if (_debugMode)
Debug.Log($"[HallucinationTrigger] Added {_insanityAmount:F2} insanity. New sanity: {HallucinationManager.Instance.SanityLevel:F2}", this);
Debug.Log($"[HallucinationTrigger] Added {_insanityAmount:F2} insanity. Sanity now: {HallucinationManager.Instance.SanityLevel:F2}", this);
}
if (_hallucinationEvent != null)
@@ -4,30 +4,66 @@ using Unheimlich.Core.Events;
namespace Unheimlich.Features.Hallucination
{
/// <summary>
/// Fires a GameEventTrigger (and therefore a HallucinationTrigger) when the player
/// enters a physics trigger collider. Pair with GameEventTrigger + HallucinationTrigger
/// on the same GameObject.
/// Self-contained zone that triggers a hallucination effect when the player enters.
/// Handles the once-flag, optional delay, insanity increase, and a discrete HallucinationEvent
/// — no GameEventTrigger or HallucinationTrigger required alongside it.
/// </summary>
[RequireComponent(typeof(GameEventTrigger))]
public class HallucinationTriggerZone : TriggerZoneBase
{
[Header("Zone Behaviour")]
[Tooltip("Allow the zone to fire again after the player exits and re-enters")]
[SerializeField] private bool _retriggerable = false;
[Tooltip("Delay in seconds before the hallucination fires after the player enters")]
[SerializeField] private float _delay = 0f;
[Header("Sanity")]
[Tooltip("Amount of insanity added when the zone fires (0 = no change)")]
[SerializeField, Range(0f, 1f)] private float _insanityAmount = 0f;
[Header("Hallucination Event")]
[Tooltip("Optional discrete hallucination event (ghost word, vision flash, etc.)")]
[SerializeField] private HallucinationEvent _hallucinationEvent;
[Header("Debug")]
[SerializeField] private bool _debugMode;
protected override bool IsRetriggerable => _retriggerable;
private GameEventTrigger _eventTrigger;
protected override void Awake()
{
base.Awake();
_eventTrigger = GetComponent<GameEventTrigger>();
}
protected override void OnPlayerEntered(Collider playerCollider)
{
_eventTrigger.Fire();
if (_delay > 0f)
Invoke(nameof(Fire), _delay);
else
Fire();
}
/// <summary>
/// Fires the hallucination effect. Can also be called from external scripts or UnityEvents.
/// </summary>
public void Fire()
{
if (HallucinationManager.Instance == null)
{
Debug.LogError("[HallucinationTriggerZone] HallucinationManager not found in scene!", this);
return;
}
if (_insanityAmount > 0f)
{
HallucinationManager.Instance.AddInsanity(_insanityAmount);
if (_debugMode)
Debug.Log($"[HallucinationTriggerZone] Added {_insanityAmount:F2} insanity on '{gameObject.name}'.", this);
}
if (_hallucinationEvent != null)
{
HallucinationManager.Instance.TriggerHallucinationEvent(_hallucinationEvent);
if (_debugMode)
Debug.Log($"[HallucinationTriggerZone] Fired event: {_hallucinationEvent.Label}", this);
}
}
}
}