using NUnit.Framework; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; using UnityStandardAssets.Characters.ThirdPerson; public class StatueBehaviour : MonoBehaviour { #region Visibility Variables [SerializeField] private float _viewRadius; [SerializeField] private float _viewAngle; public LayerMask playerMask; public LayerMask obstacleMask; #endregion public Animator statueAnimator; public NavMeshAgent statueAI; [SerializeField] ThirdPersonCharacter thirdPerson; [SerializeField] Transform _player; [SerializeField] public Transform _statue; //[SerializeField] private List _statueSkinnedMeshRenderer; [SerializeField] private float _stMovementSpeed; [SerializeField] float killRange = 0.5f; // Example kill range; adjust as necessary #region VariantsForPatrol [SerializeField] private List patrolPoints; // List of patrol points private int _currentPatrolIndex = 0; // Current patrol point index private bool _canChangePoint = true; // Flag to control when to move to the next point private bool _forceToFreezeStatue; #endregion Coroutine waitBeforeChaseCoroutine; public StatueDetection statueDetection; public enum StatueState { Spawn, Patrol, Chase, Freeze, Kill } public StatueState state = StatueState.Spawn; private void Start() { statueAI = GetComponent(); _player = PlayerManager.GetInstance().playerGameObj.transform; //statueDetection = _player.GetComponent(); // Προσθήκη του αγάλματος στη λίστα του StatueDetection if (statueDetection != null) { if (!statueDetection.statues.Contains(this.transform)) { statueDetection.statues.Add(this.transform); } } } void Update() { UpdateStatueBehaviour(); } void UpdateStatueBehaviour() { UpdateStatueMovement(); // Update movement while patrolling #region Statue's animations handling float speed = statueAI.velocity.magnitude; // Map NavMeshAgent's speed to blend tree parameter float normalizedSpeed = speed / statueAI.speed; // Adjust the divisor to match your maximum NavMeshAgent speed statueAnimator.SetFloat("speedPercent", normalizedSpeed); #endregion switch (state) { case StatueState.Spawn: if (!statueAI.isActiveAndEnabled) { this.gameObject.SetActive(true); } else { state = StatueState.Patrol; } break; case StatueState.Patrol: Patrol(patrolPoints); if (statueAI.remainingDistance > statueAI.stoppingDistance) { UpdateStatueMovement(); // Update movement while patrolling } if (CanSeePlayer()) { waitBeforeChaseCoroutine = StartCoroutine(WaitBeforeChase()); // Start the coroutine before chasing } break; case StatueState.Chase: CheckKillCondition(); if (statueDetection != null && statueDetection.CheckStatuesInView()) { // Αν ο παίκτης κοιτάζει κάποιο άγαλμα (συμπεριλαμβανομένου αυτού) state = StatueState.Freeze; statueAI.isStopped = true; // Σταμάτημα κίνησης του αγάλματος statueAnimator.SetBool("isFrozen", true); // Έναρξη animation παγώματος if (waitBeforeChaseCoroutine != null) { StopCoroutine(waitBeforeChaseCoroutine); waitBeforeChaseCoroutine = null; } } else { // Συνέχιση καταδίωξης ChasePlayer(); } break; case StatueState.Freeze: // Check if player has stopped looking if (_forceToFreezeStatue == false) { if (statueDetection != null && !statueDetection.CheckStatuesInView()) { state = StatueState.Chase; // Resume chase if player looks away } } break; case StatueState.Kill: // Logic for killing the player (not implemented) break; } } public void ForceToFreezeStatue() { _forceToFreezeStatue = true; // Αν ο παίκτης κοιτάζει κάποιο άγαλμα (συμπεριλαμβανομένου αυτού) state = StatueState.Freeze; statueAI.isStopped = true; // Σταμάτημα κίνησης του αγάλματος statueAI.SetDestination(transform.position); statueAnimator.SetBool("isFrozen", true); // Έναρξη animation παγώματος if (waitBeforeChaseCoroutine != null) { StopCoroutine(waitBeforeChaseCoroutine); waitBeforeChaseCoroutine = null; } } public void UnfreezeForcedFreezedStatue() { _forceToFreezeStatue = false; } void UpdateStatueMovement() { //If agent has not reached the desired destination : if (statueAI.remainingDistance > statueAI.stoppingDistance) { thirdPerson.Move(statueAI.desiredVelocity, false, false); } else // If agent has reached the desired destination : { thirdPerson.Move(Vector3.zero, false, false); //Agent stops moving. } } #region Field of view public Vector3 DirFromAngle(float angleInDegrees, bool angleIsGlobal) // This calculate the angle of the viewpoint { if (!angleIsGlobal) { angleInDegrees += transform.eulerAngles.y; } return new Vector3(Mathf.Sin(angleInDegrees * Mathf.Deg2Rad), 0, Mathf.Cos(angleInDegrees * Mathf.Deg2Rad)); } // This method is checking if pplayer is inside the view angle and callculates is there isnt any obstacle between the player and Medea public bool CanSeePlayer() { Collider[] targetInViewRadius = Physics.OverlapSphere(transform.position, _viewRadius, playerMask); if (targetInViewRadius.Length > 0) { _player = targetInViewRadius[0].transform; Vector3 dirToTarget = (_player.position - transform.position).normalized; if (Vector3.Angle(transform.forward, dirToTarget) < _viewAngle / 2) { float distToTarget = Vector3.Distance(transform.position, _player.position); if (!Physics.Raycast(transform.position, dirToTarget, distToTarget, obstacleMask)) { return true; } else { return false; } } } return false; } private void OnDrawGizmos() { #region GizmosForView Gizmos.color = Color.yellow; Gizmos.DrawWireSphere(transform.position, _viewRadius); Vector3 viewAngleA = DirFromAngle(-_viewAngle / 2, false); Vector3 viewAngleB = DirFromAngle(_viewAngle / 2, false); Gizmos.DrawLine(transform.position, transform.position + viewAngleA * _viewRadius); Gizmos.DrawLine(transform.position, transform.position + viewAngleB * _viewRadius); Gizmos.DrawLine(transform.position, _player.position); #endregion } #endregion #region PatrolBehaviour void Patrol(List points) { if (_canChangePoint) { // Check if the statue has reached the current destination point if (!statueAI.pathPending && statueAI.remainingDistance < 0.25f) { // Trigger the "looking around" animation statueAnimator.SetBool("IsLookingAround", true); // Stop the agent movement while waiting statueAI.isStopped = true; // Move to the next patrol point statueAI.destination = points[_currentPatrolIndex].position; // Update the patrol index to the next point _currentPatrolIndex = (_currentPatrolIndex + 1) % points.Count; // Set a flag to wait before moving to the next point _canChangePoint = false; // Start coroutine to wait before moving again StartCoroutine(WaitForNextPatrolPoint()); } } } // Coroutine to wait for a few seconds before moving to the next patrol point IEnumerator WaitForNextPatrolPoint() { yield return new WaitForSeconds(2f); // Wait time can be adjusted statueAnimator.SetBool("IsLookingAround", false); // Reset the animation statueAI.isStopped = false; // Resume movement _canChangePoint = true; // Allow changing to the next point } #endregion #region ChaseBehaviour void ChasePlayer() { statueAI.isStopped = false; statueAnimator.SetBool("IsLookingAround", false); // Stop patrol animation // Set destination to the player's position statueAI.SetDestination(_player.position); } #endregion #region WaitBeforeChase IEnumerator WaitBeforeChase() { state = StatueState.Freeze; // Temporarily freeze before chasing statueAI.isStopped = true; // Stop movement yield return new WaitForSeconds(2f); // Wait for 2 seconds statueAI.isStopped = false; // Resume movement state = StatueState.Chase; // Switch to chase state } #endregion #region Killing player private void CheckKillCondition() { //float killRange = 0.5f; // Example kill range; adjust as necessary float distanceToPlayer = Vector3.Distance(transform.position, _player.position); if (distanceToPlayer <= killRange) { KillPlayer(); // Call kill logic if within range } } private void KillPlayer() { // Disable the statue's AI to stop its behavior statueAI.enabled = false; // Disable the statue's AI (if needed) // Disable the statue's mesh renderers DisableMedeaMeshRenderers(); // Call your method to disable mesh renderers // Trigger the player death action (Low Sanity) PlayerDeath.GetInstance().LowSanityGhostKillsPlayer(); // Optional: Log the killing action for debugging Debug.Log("I killed him"); } // Method to disable mesh renderers of the statue private void DisableMedeaMeshRenderers() { // Assuming you have a list of SkinnedMeshRenderers SkinnedMeshRenderer[] meshRenderers = GetComponentsInChildren(); foreach (var renderer in meshRenderers) { renderer.enabled = false; // Disable the mesh renderer } } #endregion }