using UnityEngine;
using UnityEngine.Rendering;
namespace Unheimlich.Features.Hallucination
{
///
/// Drives a persistent post-process weight targeting either a URP/HDRP
/// and/or a exposing a _Weight property, based on an
/// internal sanity value [0, 1].
/// Wire the Wrapper methods to any UnityEvent with no arguments.
///
public class HallucinationPostProcess : MonoBehaviour
{
[Header("Targets")]
[Tooltip("URP/HDRP Volume driven by the sanity weight.")]
[SerializeField] private Volume _volume;
[Tooltip("Optional material exposing a _Weight property.")]
[SerializeField] private Material _shaderMaterial;
[Header("Sanity")]
[SerializeField, Range(0f, 1f)] private float _initialSanity = 0f;
[Tooltip("Sanity lost per second after each tween completes. 0 = no decay.")]
[SerializeField, Min(0f)] private float _autoDecayRate = 0f;
[Header("Wrappers")]
[Tooltip("Amount added when calling AddSanityWrapper().")]
[SerializeField, Range(0f, 1f)] private float _wrapperAddAmount = 0.2f;
[Tooltip("Amount removed when calling RemoveSanityWrapper().")]
[SerializeField, Range(0f, 1f)] private float _wrapperRemoveAmount = 0.2f;
[Tooltip("Transition duration used by all wrappers (seconds).")]
[SerializeField, Min(0f)] private float _wrapperDuration = 1f;
// ─── State ─────────────────────────────────────────────────────────────
private float _sanity;
private int _tweenId = -1;
private bool _decaying = false;
// ─── Lifecycle ─────────────────────────────────────────────────────────
private void Awake()
{
_sanity = Mathf.Clamp01(_initialSanity);
Apply(_sanity);
}
private void Update()
{
if (!_decaying || _autoDecayRate <= 0f || _sanity <= 0f) return;
_sanity = Mathf.Max(0f, _sanity - _autoDecayRate * Time.deltaTime);
Apply(_sanity);
if (_sanity <= 0f) _decaying = false;
}
// ─── Wrappers ──────────────────────────────────────────────────────────
/// Adds sanity over seconds.
/// Auto-decay starts when the tween completes, if is greater than 0.
public void AddSanityWrapper() => TweenSanity(_sanity + _wrapperAddAmount, _wrapperDuration);
/// Removes sanity over seconds.
public void RemoveSanityWrapper() => TweenSanity(_sanity - _wrapperRemoveAmount, _wrapperDuration);
/// Snaps sanity to 0 instantly and stops decay.
public void ResetSanityWrapper()
{
CancelCurrent();
_decaying = false;
_sanity = 0f;
Apply(0f);
}
// ─── Private ───────────────────────────────────────────────────────────
private void TweenSanity(float target, float duration)
{
target = Mathf.Clamp01(target);
_decaying = false;
if (duration <= 0f)
{
CancelCurrent();
_sanity = target;
Apply(target);
if (_autoDecayRate > 0f) _decaying = true;
return;
}
CancelCurrent();
float from = _sanity;
_tweenId = LeanTween.value(gameObject, from, target, duration)
.setEase(LeanTweenType.easeInOutQuad)
.setOnUpdate((float w) =>
{
_sanity = w;
Apply(w);
})
.setOnComplete(() =>
{
// Start decay only after the tween has fully completed.
if (_autoDecayRate > 0f) _decaying = true;
}).id;
}
private void Apply(float weight)
{
if (_volume != null) _volume.weight = weight;
if (_shaderMaterial != null) _shaderMaterial.SetFloat("_Weight", weight);
}
private void CancelCurrent()
{
if (_tweenId == -1) return;
LeanTween.cancel(_tweenId);
_tweenId = -1;
}
}
}