using System.Collections.Generic; using System.Collections; using UnityEngine; using UnityEngine.AI; public class PlayerController : CharacterBase { public Display WildDisplay; public Display3DFriend FriendDisplay; [SerializeField] protected MeleeAttackCollider AttackCollider; [Header("Camera")] public Camera Camera; public Transform CameraTarget; public float CameraDistanceMax; public float CameraDistance; public LayerMask CameraCollisionMask; [Header("Effects")] [SerializeField] private ParticleSystem SandstarParticles; [SerializeField] private ParticleSystem DeathParticles; private List _interactablesInRange = new List(); private NavMeshAgent _agent; private DialogueController _currentDialogue; private bool _lastAttackState = false; public bool IsAttacking() { if (Display.GetAnimatorLayersCount() < 1) return false; return Display.GetCurrentAnimatorStateInfo(1).IsName("Attack"); } public bool IsRecoil() { return Display.GetCurrentAnimatorStateInfo(0).IsName("Hit"); } protected override void Awake() { base.Awake(); _agent = GetComponent(); } protected override void Start() { if(GameManager.Instance != null) { GameManager.Instance.Player = this; } } protected override void Update() { #if UNITY_EDITOR if (Input.GetKeyDown(Controls.Debug_K)) { TakeDamage(9999); } #endif if (!IsAlive() || !_cc.enabled || _currentDialogue != null) return; Vector3 input = new Vector3(Input.GetAxis(Controls.XAxis), 0, Input.GetAxis(Controls.YAxis)); bool isAttacking = IsAttacking(); bool isRecoil = IsRecoil(); if (_lastAttackState != isAttacking && !isAttacking) { AttackEnd(); } if (isRecoil) { if (isAttacking) { Display.SetEvent("InterruptAttack", true); } input = Vector3.zero; } if (Input.GetKeyDown(Controls.Interact)) { if (TryInteract()) { return; } else if (!isAttacking) { AttackStart(); } } if (isAttacking) { input /= 2; } _cc.SimpleMove(transform.forward * input.z * Speed); Display.SetFloat("Input", input.z); if (input != Vector3.zero) { _cc.transform.Rotate(Vector3.up, input.x * 120 * Time.deltaTime); } if (input.z != 0) { PlayFootstep(); } _lastAttackState = isAttacking; } void LateUpdate() { RaycastHit hit; if (Physics.Raycast(CameraTarget.position, -CameraTarget.forward, out hit, CameraDistanceMax, CameraCollisionMask.value)) { CameraDistance = hit.distance; } else { CameraDistance = Mathf.Lerp(CameraDistance, CameraDistanceMax, 0.5f); } Camera.transform.position = CameraTarget.position - CameraTarget.forward * CameraDistance; } private bool TryInteract() { for (int i = _interactablesInRange.Count - 1; i>=0; i--) { if(_interactablesInRange[i] == null) { _interactablesInRange.RemoveAt(i); } } if (_interactablesInRange.Count == 0) return false; if (!_interactablesInRange[0].Interact(this)) return false; Display.PlayState("Interact"); return true; } private void AttackStart() { Display.SetEvent("InterruptAttack", false); AttackCollider.Toggle(true); AttackCollider.OnHit.AddListener((target) => { if ((PlayerController)target == this) return; target.TakeDamage(1); }); Display.SetLayerWeight(1, 1); Display.PlayState("Attack", 1); } private void AttackEnd() { if (AttackCollider.gameObject.activeSelf) { AttackCollider.Toggle(false); AttackCollider.OnHit.RemoveAllListeners(); } Display.SetLayerWeight(1, 0); } public void ToggleCharacterController(bool enabled) { _cc.enabled = enabled; _interactablesInRange.Clear(); } public void ToggleSandstarEmission(bool value) { var emission = SandstarParticles.emission; emission.enabled = value; } public override void TakeDamage(int dmg) { base.TakeDamage(dmg); if (IsAlive()) { Display.PlayState("Hit"); } } protected override void OnDeath() { ToggleCharacterController(false); Display.PlayState("Die"); StartCoroutine(PlayDeathSequence(2)); } private IEnumerator PlayDeathSequence(float delay) { yield return new WaitForSeconds(delay); FriendDisplay.gameObject.SetActive(false); DeathParticles.gameObject.SetActive(true); yield return new WaitForSeconds(0.5f); transform.SetParent(null); WildDisplay.gameObject.SetActive(true); Display = WildDisplay; yield return PathfindToEntrance(); } private IEnumerator PathfindToEntrance() { var entrance = GameManager.Instance.CurrentExit; if(entrance == null) { entrance = GameManager.Instance.Exits[0]; } ToggleCharacterController(false); float timeOut = 2; //seconds _agent.enabled = true; _agent.SetDestination(entrance.GetComponent().GetExitLocator().position); Display.SetFloat("Input", 1); do { timeOut -= Time.deltaTime; yield return 0; } while ((_agent.remainingDistance > _agent.stoppingDistance) && timeOut > 0); Display.SetFloat("Input", 0); Debug.Log("Pathfound"); if(string.IsNullOrEmpty(GameManager.Instance.PreviousExit.TargetScene)) { Debug.LogError("Previous scene not found! Using default"); GameManager.ChangeSceneAsync(entrance.GetComponent().GetDefaultExit()); } else { GameManager.ChangeSceneAsync(GameManager.Instance.PreviousExit); } } public void SetDialogue(DialogueController controller) { Display.SetFloat("Input", 0); _currentDialogue = controller; } void OnControllerColliderHit(ControllerColliderHit hit) { var triggers = hit.gameObject.GetComponent(); if (triggers != null && triggers.CollisionTags.Contains(this.tag)) { triggers.OnCollisionEnterAction.Invoke(); } } void OnTriggerEnter(Collider other) { var interactable = other.GetComponent(); if (interactable != null) { _interactablesInRange.Add(interactable); } } void OnTriggerExit(Collider other) { var interactable = other.GetComponent(); if (interactable != null && _interactablesInRange.Contains(interactable)) { _interactablesInRange.Remove(interactable); } } }