using System; using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine; using UnityEngine.AI; public class BlockingAnimation : Attribute { } public class Player : MonoBehaviour { public static Player Instance { get; private set; } [SerializeField] public NavMeshAgent _navAgent; [SerializeField] public Animator _animator; [SerializeField] private Transform _holdPoint; private PlayerStates _currentActing; private AnimationStates _currentAnimation; public Dictionary Stats; public JobPositions JobPosition { get; set; } private readonly Queue _tasks = new Queue(); private PlayerTasks _currentTask; private const string WALK_VELOCITY = "WalkVelocity"; private ContainerItem _containerItem; private Action _OnAnimationFinish; private void Awake() { if (Instance != null) { Destroy(gameObject); Debug.Log("There's more than one player instance"); return; } PlayerPrefs.SetString("lastExitName", string.Empty); Instance = this; Stats = PlayerStats.CreateInitialStats(); JobPosition = JobPositions.Unemployed; DontDestroyOnLoad(gameObject); } private void Start() { TimeManager.Instance.OnMinuteChanged += UpdateStatsByClock; _animator.applyRootMotion = true; _navAgent.updatePosition = false; _currentActing = PlayerStates.Awake; } private void OnDestroy() { TimeManager.Instance.OnMinuteChanged -= UpdateStatsByClock; } private void Update() { if (PlayerHelper.IsBlockingAnimation(_currentAnimation)) { if (IsAnimationStatePlaying(0)) { return; } else { _OnAnimationFinish?.Invoke(); _OnAnimationFinish = null; } } if (_currentTask == null || _currentTask.Status == TaskStatus.Complete) { _tasks.TryDequeue(out _currentTask); } if (_currentTask != null) { if (_currentTask.Status == TaskStatus.Waiting) Debug.Log($"Current task {_currentTask.Task}"); switch (_currentTask.Task) { case Tasks.Rotate: _currentTask.UpdateStatus(Rotate(_currentTask.TagretObject._interactionPoint.forward)); break; case Tasks.Move: if (_currentAnimation == AnimationStates.Sitting) { SetPlayerAnimation(AnimationStates.Standing); return; } _navAgent.SetDestination(_currentTask.TagretObject._interactionPoint.position); _currentTask.UpdateStatus(MoveToPoint()); break; case Tasks.Interact: // Show interaction menu _currentTask.TagretObject.SpawnMenu(); _currentTask = null; //if (IsPathComplete(_currentTask.TagretObject._interactionPoint.position)) // _currentTask.UpdateStatus(InteractWithObject(_currentTask.TagretObject)); //else //{ // AddTask(new PlayerTasks(Tasks.Move, _currentTask.TagretObject)); // AddTask(new PlayerTasks(Tasks.Rotate, _currentTask.TagretObject)); // AddTask(_currentTask); // _currentTask = null; //} break; } } } public void SetPosition(Vector3 desiredPosition) { _navAgent.Warp(desiredPosition); _navAgent.updatePosition = false; } private TaskStatus MoveToPoint() { _navAgent.isStopped = false; SetPlayerAnimation(AnimationStates.Walking); _animator.SetFloat(WALK_VELOCITY, _navAgent.velocity.magnitude); return IsPathComplete(_navAgent.destination) ? TaskStatus.Complete : TaskStatus.InProgress; } private bool IsPathComplete(Vector3 destination) { var dest = new Vector3(destination.x, 0, destination.z); var pos = new Vector3(_navAgent.transform.position.x, 0, _navAgent.transform.position.z); if (Vector3.Distance(dest, pos) <= _navAgent.radius) { transform.position = destination; if (!_navAgent.hasPath || _navAgent.velocity.sqrMagnitude < 0.2f) { SetPlayerAnimation(AnimationStates.Idle); _navAgent.isStopped = true; return true; } } return false; } public TaskStatus Rotate(Vector3 target) { var targetRot = Quaternion.LookRotation(target); Quaternion rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(target), 10 * Time.deltaTime); rotation.x = 0; transform.rotation = rotation; if (IsApproximate(targetRot, transform.rotation, 0.000004f)) { return TaskStatus.Complete; } return TaskStatus.InProgress; } private bool IsApproximate(Quaternion q1, Quaternion q2, float precision) { return Mathf.Abs(Quaternion.Dot(q1, q2)) >= 1 - precision; } private TaskStatus InteractWithObject(BaseInteractableObject interactableObject) { interactableObject.Interact(this); return TaskStatus.Complete; } public void SetPlayerAnimation(AnimationStates newState, Action onAnimationFinish) { _OnAnimationFinish = onAnimationFinish; SetPlayerAnimation(newState); } public void SetPlayerAnimation(AnimationStates newState) { if (newState == _currentAnimation) { return; } _animator.Play(PlayerHelper.GetEnumMemberValue(newState)); _currentAnimation = newState; } public bool IsAnimationStatePlaying(int animLayer) { string stateName = PlayerHelper.GetEnumMemberValue(_currentAnimation); var stateInfo = _animator.GetCurrentAnimatorStateInfo(animLayer); return stateInfo.IsName(stateName) && stateInfo.normalizedTime < 1.0f; } public void AddTask(PlayerTasks task) { _tasks.Enqueue(task); } public void SetPlayerActing(PlayerStates state) { _currentActing = state; } public void UpdateStatsByClock() { switch (_currentActing) { case PlayerStates.Eating: Stats[StatsId.Food].increase(10f); break; case PlayerStates.Sleeping: Stats[StatsId.Energy].increase(0.2f); break; default: Stats[StatsId.Food].deduct(0.034f); // 48 hours it's 100, 100/2880=~0.034 per minute Stats[StatsId.Energy].deduct(0.1f); // 24 hours it's 100, 100/1440=~0.096 per minute break; } } public void Pay(float amount) { Stats[StatsId.Money].deduct(amount); } public void AddMoney(float amount) { Stats[StatsId.Money].increase(amount); } public void SetContainerItem(ContainerItem containerItem) { containerItem.transform.parent = _holdPoint; containerItem.transform.localPosition = Vector3.zero; _containerItem = containerItem; } public ContainerItem GetContainerItem() { return _containerItem; } public void ClearContainerItem() { Destroy(_containerItem.gameObject); _containerItem = null; } public bool IsHoldContainerItem() { return _containerItem != null; } }