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

336 lines
11 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<SkinnedMeshRenderer> _statueSkinnedMeshRenderer;
[SerializeField] private float _stMovementSpeed;
[SerializeField] float killRange = 0.5f; // Example kill range; adjust as necessary
#region VariantsForPatrol
[SerializeField] private List<Transform> 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<NavMeshAgent>();
_player = PlayerManager.GetInstance().playerGameObj.transform;
//statueDetection = _player.GetComponent<StatueDetection>();
// Προσθήκη του αγάλματος στη λίστα του 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<Transform> 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<SkinnedMeshRenderer>();
foreach (var renderer in meshRenderers)
{
renderer.enabled = false; // Disable the mesh renderer
}
}
#endregion
}