Files
HauntedBloodlines/Assets/Scripts/Animations Scripts/HallucinationsEye.cs
2025-05-29 22:31:40 +03:00

379 lines
12 KiB
C#

using UnityEngine;
using System.Collections;
using UnityEngine.Animations.Rigging;
using UnityEngine.VFX;
using System.Collections.Generic;
[System.Serializable]
public class IrisData
{
public SkinnedMeshRenderer meshRenderer;
public int blendShapeIndex;
public float currentValue;
}
[System.Serializable]
public class BurnableEye
{
public Renderer mainRenderer;
public Renderer eyeballRenderer;
public float burnLerpValue;
}
public class HallucinationsEye : MonoBehaviour
{
public bool IsAlive { get; private set; } = true;
public bool IsWatching { get; private set; } = false;
public bool IsOutOfWall { get; private set; } = false;
[Header("References")]
public List<Transform> eyePivots = new List<Transform>();
public Animator eyeAnimator;
public AudioSource eyeAudioSource;
public List<MultiAimConstraint> eyeMultiAimConstraints = new List<MultiAimConstraint>();
private float targetAimWeight = 1f;
public float aimWeightChangeSpeed = 2f;
public RigBuilder eyeRigBuilder;
public Renderer[] eyeRenderers;
[Header("Gaze Settings")]
public float gazeDistanceThreshold = 8f;
public float gazeFOV = 90f;
[Header("Iris Control")]
public List<IrisData> irisEyes = new List<IrisData>();
public float minIrisValue = 0f;
public float maxIrisValue = 100f;
public float defaultIrisValue = 70f;
public float maxIrisDistance = 10f;
public float irisSmoothSpeed = 5f;
public float irisLightFactor = 0f;
public bool isBeingLit = false;
[Header("Effects")]
public VisualEffect smokeEffect;
private bool IsPlayingSmokeEffect;
[Header("Eye Material Burn")]
public List<BurnableEye> burnableEyes = new List<BurnableEye>();
public string lerpProperty = "_Lerp";
private bool isBurning = false;
[Header("Debug Visualization")]
public bool showIrisDebug = true;
public Vector3 debugOffset = new Vector3(0, 1f, 0);
[Header("Audio")]
public AudioClip[] eyeMovementClips;
public float audioFadeDuration = 0.5f;
private Coroutine fadeInCoroutine;
private Coroutine fadeOutCoroutine;
private bool shouldPlayAudioClip = true;
[Header("Eye Colliders")]
public Collider[] eyeColliders;
private Dictionary<Collider, int> colliderToEyeIndex;
private void Awake()
{
if (eyeColliders == null || eyeColliders.Length == 0)
eyeColliders = GetComponentsInChildren<Collider>();
colliderToEyeIndex = new Dictionary<Collider, int>();
for (int i = 0; i < eyeColliders.Length; i++)
colliderToEyeIndex[eyeColliders[i]] = i;
}
public bool OwnsCollider(Collider col)
{
return colliderToEyeIndex != null && colliderToEyeIndex.ContainsKey(col);
}
public bool TryGetEyeIndex(Collider col, out int index)
{
if (colliderToEyeIndex != null)
return colliderToEyeIndex.TryGetValue(col, out index);
index = -1;
return false;
}
private void Start()
{
eyePivots.Clear();
foreach (var c in eyeMultiAimConstraints)
{
if (c != null && c.data.constrainedObject != null)
eyePivots.Add(c.data.constrainedObject);
}
foreach (var iris in irisEyes)
iris.currentValue = defaultIrisValue;
StartCoroutine(AfterGameLoaded());
if (eyeAnimator == null) eyeAnimator = GetComponent<Animator>();
if (eyeAudioSource == null) eyeAudioSource = GetComponent<AudioSource>();
}
IEnumerator AfterGameLoaded()
{
yield return new WaitUntil(() => LoadManager.GetInstance() != null && !LoadManager.GetInstance().LoadingGame);
yield return new WaitUntil(() => EyesManager.GetInstance() != null);
EyesManager.GetInstance().RegisterEye(this);
SetEyeTarget(PlayerManager.GetInstance().playerHead.transform);
}
private void SetEyeTarget(Transform newTarget)
{
if (eyeMultiAimConstraints.Count == 0 || newTarget == null) return;
foreach (var c in eyeMultiAimConstraints)
{
var src = new WeightedTransformArray();
src.Add(new WeightedTransform(newTarget, 1f));
c.data.sourceObjects = src;
}
eyeRigBuilder?.Build();
}
private void Update()
{
UpdateIris();
UpdateEyeGaze();
UpdateAimWeight();
UpdateBurnVisual();
}
public void CheckPlayerVisibility(Transform player, float fov, float distance)
{
if (!IsAlive) return;
bool visible = false;
foreach (var col in eyeColliders)
{
Vector3 origin = col.transform.position;
Vector3 toPlayer = player.position - origin;
float d = toPlayer.magnitude;
if (d > distance) continue;
float ang = Vector3.Angle(col.transform.forward, toPlayer.normalized);
if (ang > fov * 0.5f) continue;
if (Physics.Raycast(origin, toPlayer.normalized, out RaycastHit hit, distance))
{
if (hit.transform == player) { visible = true; break; }
}
}
}
public void EyeIsWatching()
{
eyeAnimator.SetBool("EyeIsFadingIn", true);
eyeAnimator.SetBool("EyeIsFadingOut", false);
IsOutOfWall = true; IsWatching = true;
}
public void EyeIsNoLongerWatching()
{
eyeAnimator.SetBool("EyeIsFadingIn", false);
eyeAnimator.SetBool("EyeIsFadingOut", true);
IsOutOfWall = false; IsWatching = false;
}
public void EvaluateLightingFrom(Transform lightT, float spotAngle, float maxDist)
{
if (!CandleController.GetInstance().isCandleOn)
{
isBeingLit = false; irisLightFactor = 0f;
return;
}
Vector3 toEye = transform.position - lightT.position;
float d = toEye.magnitude;
if (d > maxDist)
{
isBeingLit = false; irisLightFactor = 0f;
return;
}
float ang = Vector3.Angle(lightT.forward, toEye.normalized);
float cf = 1f - Mathf.Clamp01(ang / (spotAngle * 0.5f));
isBeingLit = cf > 0f;
irisLightFactor = Mathf.Clamp01(cf);
}
private void UpdateIris()
{
if (irisEyes.Count == 0 || EyesManager.GetInstance().player == null) return;
float target = defaultIrisValue;
if (isBeingLit)
{
float dist = Vector3.Distance(EyesManager.GetInstance().player.position, transform.position);
float df = 1f - Mathf.Clamp01(dist / maxIrisDistance);
float inf = irisLightFactor * df;
target = Mathf.Lerp(maxIrisValue, minIrisValue, inf);
}
foreach (var iris in irisEyes)
{
iris.currentValue = Mathf.Lerp(iris.currentValue, target, Time.deltaTime * irisSmoothSpeed);
if (iris.meshRenderer != null && iris.blendShapeIndex >= 0)
iris.meshRenderer.SetBlendShapeWeight(iris.blendShapeIndex, iris.currentValue);
}
}
private void UpdateEyeGaze()
{
if (eyeMultiAimConstraints.Count == 0 || EyesManager.GetInstance().player == null) return;
Transform player = EyesManager.GetInstance().player;
Vector3 toP = player.position - transform.position;
float d = toP.magnitude;
float ang = Vector3.Angle(transform.forward, toP.normalized);
for (int i = 0; i < eyeMultiAimConstraints.Count; i++)
{
float tw = (d <= gazeDistanceThreshold && ang <= gazeFOV * 0.5f) ? 1f : 0f;
eyeMultiAimConstraints[i].weight = Mathf.Lerp(eyeMultiAimConstraints[i].weight, tw, Time.deltaTime * 5f);
}
}
private void UpdateAimWeight()
{
foreach (var c in eyeMultiAimConstraints)
c.weight = Mathf.Lerp(c.weight, targetAimWeight, Time.deltaTime * aimWeightChangeSpeed);
}
public void UpdateBurnVisual()
{
UpdateBurnVisual(0f);
}
public void UpdateBurnVisual(float normalizedBurnProgress, int eyeIndex = -1)
{
if (eyeIndex >= 0 && eyeIndex < burnableEyes.Count)
{
var eye = burnableEyes[eyeIndex];
float val = isBurning
? Mathf.Clamp01(normalizedBurnProgress)
: Mathf.Lerp(eye.burnLerpValue, 0f, Time.deltaTime * 2f);
eye.burnLerpValue = val;
if (eye.mainRenderer != null) eye.mainRenderer.material.SetFloat(lerpProperty, val);
if (eye.eyeballRenderer != null) eye.eyeballRenderer.material.SetFloat(lerpProperty, val);
}
else
{
foreach (var eye in burnableEyes)
{
float val = isBurning
? Mathf.Clamp01(normalizedBurnProgress)
: Mathf.Lerp(eye.burnLerpValue, 0f, Time.deltaTime * 2f);
eye.burnLerpValue = val;
if (eye.mainRenderer != null) eye.mainRenderer.material.SetFloat(lerpProperty, val);
if (eye.eyeballRenderer != null) eye.eyeballRenderer.material.SetFloat(lerpProperty, val);
}
}
}
public void SetBurningState(bool burning, int eyeIndex = -1)
{
if (eyeIndex >= 0 && eyePivots.Count > 1)
{
string param = eyeIndex == 0 ? "Eye1IsGettingBurned"
: eyeIndex == 1 ? "Eye2IsGettingBurned"
: eyeIndex == 2 ? "Eye3IsGettingBurned" : null;
if (param != null)
eyeAnimator.SetBool(param, burning);
}
if (eyePivots.Count == 1)
{
eyeAnimator.SetBool("EyeIsGettingBurned", burning);
}
isBurning = burning;
targetAimWeight = burning ? 0f : 1f;
}
public void BurnEye()
{
if (!IsAlive) return;
eyeAnimator.SetBool("EyeIsGettingBurned", false);
eyeAnimator.SetBool("EyeIsDying", true);
eyeAudioSource.Stop();
SetSmokeActive(false);
IsAlive = false;
IsWatching = false;
}
public void ResetEye()
{
IsAlive = true;
IsWatching = false;
foreach (var r in eyeRenderers)
r.enabled = true;
eyeAnimator.SetBool("EyeIsDying", false);
switch (eyePivots.Count)
{
case 1:
eyeAnimator.SetBool("EyeIsGettingBurned", false);
break;
case 2:
eyeAnimator.SetBool("Eye1IsGettingBurned", false);
eyeAnimator.SetBool("Eye2IsGettingBurned", false);
break;
case 3:
eyeAnimator.SetBool("Eye1IsGettingBurned", false);
eyeAnimator.SetBool("Eye2IsGettingBurned", false);
eyeAnimator.SetBool("Eye3IsGettingBurned", false);
break;
}
}
public void SetSmokeActive(bool active)
{
if (smokeEffect == null) return;
if (active && !IsPlayingSmokeEffect)
{
smokeEffect.Play();
IsPlayingSmokeEffect = true;
}
else if (!active && IsPlayingSmokeEffect)
{
smokeEffect.Stop();
IsPlayingSmokeEffect = false;
}
}
private void OnDestroy()
{
if (EyesManager.GetInstance() != null)
EyesManager.GetInstance().UnregisterEye(this);
}
private void OnDrawGizmosSelected()
{
if (!IsAlive || eyePivots.Count == 0) return;
Gizmos.color = IsWatching ? Color.red : Color.yellow;
foreach (var pivot in eyePivots)
{
Vector3 origin = pivot.position;
Vector3 forward = pivot.forward;
float halfFOV = gazeFOV * 0.5f;
float radius = gazeDistanceThreshold;
int seg = 20;
for (int i = 0; i <= seg; i++)
{
float angle = -halfFOV + i * (gazeFOV / seg);
Vector3 dir = Quaternion.Euler(0, angle, 0) * forward;
Gizmos.DrawLine(origin, origin + dir * radius);
}
}
DrawIrisDebugBar();
}
private void DrawIrisDebugBar()
{
if (!showIrisDebug || irisEyes.Count == 0) return;
Color c = Color.Lerp(Color.red, Color.green, irisLightFactor);
Gizmos.color = c;
Vector3 pos = transform.position + debugOffset;
float len = 1f * irisLightFactor;
Vector3 left = pos - transform.right * 0.5f;
Vector3 right = left + transform.right * len;
Gizmos.DrawCube((left + right) * 0.5f, new Vector3(len, 0.1f, 0.01f));
}
}