using Characters.Enemy.States; using Koptilnya.StateMachine; using Mirror; using UnityEngine; using UnityEngine.AI; using UnityEngine.Animations.Rigging; using UnityEngine.Serialization; namespace Characters.Enemy { public class Enemy : Pawn { public NavMeshAgent agent; public Animator animator; public Rig aimRig; public AudioSource audioSource; public AudioSource footstepAudioSource; public ParticleSystem hitVFX; public Transform aimTransform; public float aggressionDistance = 5f; [SyncVar] public EnemyState state = EnemyState.Idle; [SyncVar, HideInInspector] public float aimRigWeight; public readonly StateMachine stateMachine = new StateMachine(); [FormerlySerializedAs("target")] public Transform targetTransform; public NetworkAnimator networkAnimator; public bool HasTarget => targetTransform != null; public float DistanceToTarget => Vector3.Distance(targetTransform.transform.position, transform.position); private CustomNetworkManager _networkManager; private NetworkGameManager _networkGameManager; private float _targetAimRigWeight; private float _stepCycle; private AudioClip _footstepClip; private Vector3 _footstepPrevPosition; private const float StepDelay = 0.12f; private const float RunSpeed = 4.974f; private static readonly int SpeedAnimHash = Animator.StringToHash("speed"); void Start() { _networkManager = NetworkManager.singleton.GetComponent(); _networkGameManager = NetworkGameManager.singleton; _footstepClip = Resources.Load("Audio/EnemySounds/Steps/monsterStep1"); networkAnimator = GetComponent(); stateMachine.Add(new IdleState(this)); stateMachine.Add(new PatrolState(this)); stateMachine.Add(new ChaseState(this)); stateMachine.Add(new StunnedState(this)); stateMachine.Add(new AttackState(this)); stateMachine.SetCurrentState(state); } void Update() { Debug.DrawLine(transform.position, transform.forward * aggressionDistance, Color.green, 0f); if (isClient) { Footsteps(); } if (isServer) { agent.speed = _speedMul * RunSpeed; animator.SetFloat(SpeedAnimHash, agent.velocity.magnitude); } aimRigWeight = isServer ? Mathf.Lerp(aimRigWeight, _targetAimRigWeight, Time.deltaTime) : aimRigWeight; aimRig.weight = aimRigWeight; stateMachine.Update(); } void FixedUpdate() { stateMachine.FixedUpdate(); } public void SetAimRigWeight(float value) { _targetAimRigWeight = value; } void Footsteps() { float velocity = ((_footstepPrevPosition - transform.position) / Time.deltaTime).magnitude; float clampedVelocity = Mathf.Clamp(RunSpeed / velocity + 1, 1, RunSpeed); if (Time.time > _stepCycle && velocity > 0) { footstepAudioSource.pitch = 1f + Random.Range(-0.1f, 0.1f); footstepAudioSource.PlayOneShot(_footstepClip); _stepCycle = Time.time + (clampedVelocity * StepDelay); } _footstepPrevPosition = transform.position; } [ClientRpc] public void RpcKillTarget(Transform target) { Interactions interactions = target.GetComponent(); if (interactions != null) interactions.DropProp(); target.GetComponent().Die(); } [ServerCallback] public override void TakeDamage() { base.TakeDamage(); ChangeState(EnemyState.Stunned); } public void ChangeState(EnemyState newState) { state = newState; stateMachine.SetCurrentState(newState); } public Transform GetClosestTarget() { var alivePlayers = _networkManager.alive; if (HasTarget && alivePlayers.Count == 1 && alivePlayers[0].transform == targetTransform) { return targetTransform; } Transform closestTarget = null; float sortDistance = float.MaxValue; foreach (var player in _networkManager.alive) { float distance = Vector3.Distance(player.transform.position, transform.position); if (distance <= aggressionDistance && distance < sortDistance && agent.SetDestination(player.transform.position)) { sortDistance = distance; closestTarget = player.transform; } } return closestTarget; } } }