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 eyePivots = new List(); public Animator eyeAnimator; public AudioSource eyeAudioSource; public List eyeMultiAimConstraints = new List(); 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 irisEyes = new List(); 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 burnableEyes = new List(); 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 colliderToEyeIndex; private void Awake() { if (eyeColliders == null || eyeColliders.Length == 0) eyeColliders = GetComponentsInChildren(); colliderToEyeIndex = new Dictionary(); 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(); if (eyeAudioSource == null) eyeAudioSource = GetComponent(); } 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)); } }