using Assets.Scripts.Actions.Interfaces; using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public enum Tasks { Move, Interact }; public enum TaskStatus { Waiting, InProgress, Complete }; public enum ActionStates { Idle, Walking, Sleeping, Sitting }; public class Player : MonoBehaviour { public event EventHandler OnPlayerMoves; public static Player Instance { get; private set; } [SerializeField] public NavMeshAgent _navAgent; [SerializeField] public Animator _animator; public bool allowMovement = true; private ActionStates _currentState; private Vector3 _groundDeltaPosition; public Dictionary PlayerStats; public IWorkPlace WorkPlace { get; set; } private readonly Queue _tasks = new Queue(); private PlayerTasks _currentTask; private const string FALL_DOWN = "Fall"; private const string SIT_DOWN = "SitDown"; private const string STAND_UP = "StandUp"; private const string WALK = "Walk"; private const string WALK_VELOCITY = "WalkVelocity"; private void Awake() { if (Instance != null) { Debug.Log("There's more than one player instance"); return; } Instance = this; } private void Start() { TimeManager.OnMinuteChanged += UpdatePlayerState; allowMovement = true; _navAgent.updatePosition = false; PlayerStats = GameManager.Instance.PlayerStats; } private void OnDestroy() { TimeManager.OnMinuteChanged -= UpdatePlayerState; } private void Update() { if (_currentTask == null || _currentTask.Status == TaskStatus.Complete) { _tasks.TryDequeue(out _currentTask); } if (_currentTask != null) { switch (_currentTask.Task) { case Tasks.Move: OnPlayerMoves?.Invoke(this, new OnPlayerMovesEventArgs() { PointToMove = _currentTask.Position }); _navAgent.SetDestination(_currentTask.Position); _currentTask.UpdateStatus(MoveToPoint()); break; case Tasks.Interact: _currentTask.UpdateStatus(InteractWithObject(_currentTask.Interactable)); break; } } } private TaskStatus MoveToPoint() { var worldDeltaPosition = _navAgent.nextPosition - transform.position; _groundDeltaPosition.x = Vector3.Dot(transform.right, worldDeltaPosition); _groundDeltaPosition.y = Vector3.Dot(transform.forward, worldDeltaPosition); Vector2 velocity = (Time.deltaTime > 1e-5f) ? _groundDeltaPosition / Time.deltaTime : Vector2.zero; var shouldMove = velocity.magnitude > 0.025f && _navAgent.remainingDistance > _navAgent.radius; if (_currentState != ActionStates.Sleeping) { _currentState = shouldMove ? ActionStates.Walking : ActionStates.Idle; SetPlayerState(ActionStates.Walking); _animator.SetFloat(WALK_VELOCITY, velocity.y); } return pathComplete() ? TaskStatus.Complete : TaskStatus.InProgress; } private bool pathComplete() { if (Vector3.Distance(_navAgent.destination, _navAgent.transform.position) <= _navAgent.radius) { if (!_navAgent.hasPath || _navAgent.velocity.sqrMagnitude == 0f) { SetPlayerState(ActionStates.Idle); return true; } } return false; } private TaskStatus InteractWithObject(BaseInteractableObject interactableObject) { interactableObject.Interact(this); return TaskStatus.Complete; } public void SetPlayerState(ActionStates newState) { switch (newState) { case ActionStates.Idle: _animator.SetBool(WALK, false); break; case ActionStates.Walking when _currentState == ActionStates.Sitting: _animator.SetBool(SIT_DOWN, false); _animator.SetBool(SIT_DOWN, true); _animator.SetBool(WALK, true); break; case ActionStates.Walking: _animator.SetBool(WALK, true); break; case ActionStates.Sleeping: break; break; case ActionStates.Sitting: break; } _currentState = newState; } private void OnAnimatorMove() { transform.position = _navAgent.nextPosition; } public void AddTask(PlayerTasks task) { _tasks.Enqueue(task); } public void UpdatePlayerState() { PlayerStats[StatsId.Food].deduct(0.034m); // 48 hours it's 100, 100/2880=~0.034 per minute switch (_currentState) { case ActionStates.Idle: case ActionStates.Walking: PlayerStats[StatsId.Energy].deduct(0.1m); // 24 hours it's 100, 100/1440=~0.096 per minute break; case ActionStates.Sleeping: PlayerStats[StatsId.Energy].increase(1m); break; } if (PlayerStats[StatsId.Energy].Value <= 10 && _currentState != ActionStates.Sleeping) { _currentState = ActionStates.Sleeping; allowMovement = false; _animator.SetBool(FALL_DOWN, true); } if (PlayerStats[StatsId.Energy].Value >= 100 && _currentState == ActionStates.Sleeping) { _currentState = ActionStates.Idle; allowMovement = true; _animator.SetBool(FALL_DOWN, false); } } public void BuyAction(IPlayerAction action) { PlayerStats[StatsId.Money].deduct(((ISellable)action).Price); action.ApplyAction(this); } }