- using _Game.Player.StateMachine.Necro;
- using DG.Tweening;
- using UnityEngine;
- using UnityEngine.Events;
- [AddComponentMenu("Game/Player/Necromancer"), DisallowMultipleComponent()]
- public class PlayerNecromancerController : MonoBehaviour
- {
- public const float StandardJumpForce = 6.5f;
- public const float CoyoteTime = .285f;
- public const float MaxJumpTime = .35f;
- public const float JumpRememberTime = .195f;
- public const float invincibleTime = 1f;
- //refs
- [SerializeField] private InputReader _inputReader = default;
- [SerializeField] private CollisionHelper _collisionHelper;
- [SerializeField] private Rigidbody2D _rb;
- [SerializeField] private Animator _animator;
- [SerializeField] private SpriteRenderer _spriteRenderer;
- [SerializeField] private CapsuleCollider2D _capsuleCollider;
- #region FSM
- private NecroBaseState m_currentState = null;
- private readonly NecroGroundState m_groundedState = new NecroGroundState();
- private readonly NecroJumpingState m_jumpingState = new NecroJumpingState();
- private readonly NecroFallingState m_fallingState = new NecroFallingState();
- private readonly NecroDeadState m_necroDeadState = new NecroDeadState();
- public NecroGroundState GroundedState => m_groundedState;
- public NecroJumpingState JumpingState => m_jumpingState;
- public NecroFallingState FallingState => m_fallingState;
- public NecroDeadState DeadState => m_necroDeadState;
- #endregion
- [Header("Customizables"), Space(5)]
- public float moveSpeed;
- public float jumpHeight;
- public float fastFallMultiplier = 2f;
- [SerializeField, Range(42, 120)] private float airRecoveryRate = 45f;
- [Space(5)]
- [Range(0, .3f), SerializeField] private float smoothingFactor = 0.1666f;
- [Range(0, .56f), SerializeField] private float airSmoothingFactor = 0.1666f;
- [Space(5)]
- private Vector2 _previousMovementInput;
- public bool jumpInput;
- private float startGravityScale;
- private float m_jumpForce;
- private readonly Vector2 capsuleColliderDeadOffset = new Vector2(0, -0.62f);
- private Vector2 initialVerticalCapsuleSize;
- private IInteractable _interactable;
- #region Timers
- private float coyoteTimer;
- private float jumpLimitTimer;
- private float rememberJumpTimer;
- private float invincibleFrameTimer;
- #endregion
- internal Vector2 m_velocityVar;
- internal float currentXVelocity;
- [Space(5)] public UnityEvent onWizardDead;
- public bool alive = true;
- //Accesors
- public Vector2 TargetMoveSpeed => (Vector2.right * moveSpeed * _previousMovementInput);
- public Rigidbody2D Rigidbody => _rb;
- public float SmoothingFactor => smoothingFactor;
- public CollisionHelper CollsionsH => _collisionHelper;
- public float CoyoteTimer
- {
- get => coyoteTimer;
- set => coyoteTimer = value;
- }
- public float RememberJumpTimer
- {
- get => rememberJumpTimer;
- set => rememberJumpTimer = value;
- }
- public float JumpTimer => jumpLimitTimer;
- public SpriteRenderer SpriteRenderer => _spriteRenderer;
- public Animator Animator => _animator;
- private void OnEnable()
- {
- _inputReader.jumpEvent += OnJumpInitiated;
- _inputReader.jumpCanceledEvent += OnJumpCanceled;
- _inputReader.moveEvent += OnMove;
- _inputReader.interactEvent += OnInteract;
- }
- private void OnDisable()
- {
- _inputReader.jumpEvent -= OnJumpInitiated;
- _inputReader.jumpCanceledEvent -= OnJumpCanceled;
- _inputReader.moveEvent -= OnMove;
- _inputReader.interactEvent -= OnInteract;
- }
- private void Awake()
- {
- ChangeState(m_groundedState);
- startGravityScale = _rb.gravityScale;
- initialVerticalCapsuleSize = _capsuleCollider.size;
- }
- private void Start()
- {
- GameManager.Instance.onGameOver.AddListener(() =>
- { this.enabled = false; });
- }
- private void OnInteract()
- {
- //ToDo: For Talking
- //Should work on any state but Dead
- if (ReferenceEquals(m_currentState, m_necroDeadState) || PauseManager.paused)
- {
- Debug.Log("Invalid State");
- return;
- }
- if (!ReferenceEquals(_interactable, null)) _interactable.Interact();
- }
- private void OnMove(Vector2 movement) { _previousMovementInput = movement; }
- private void OnJumpInitiated()
- {
- jumpInput = true;
- rememberJumpTimer = JumpRememberTime;
- m_currentState.OnPlayerJump(this);
- }
- private void OnJumpCanceled() { jumpInput = false; }
- void Update()
- {
- if (PauseManager.paused) return;
- //Limit Jumps
- if (jumpLimitTimer > 0)
- jumpLimitTimer -= Time.deltaTime;
- if (invincibleFrameTimer > 0)
- invincibleFrameTimer -= Time.deltaTime;
- m_currentState.Update(this);
- _rb.velocity = new Vector2(_rb.velocity.x, Mathf.Clamp(_rb.velocity.y, -10, 20f));
- }
- private void FixedUpdate()
- {
- if (PauseManager.paused) return;
- m_currentState.FixedUpdate(this);
- }
- public void HandleSpriteFlip()
- {
- if (Mathf.Abs(_previousMovementInput.x) >= float.Epsilon)
- _spriteRenderer.flipX = _previousMovementInput.x < 0 ? true : false;
- }
- public void Walk()
- {
- var x = Mathf.SmoothDamp(_rb.velocity.x, TargetMoveSpeed.x, ref currentXVelocity, SmoothingFactor);
- _rb.velocity = new Vector2(x, _rb.velocity.y);
- }
- public void HandleMovementAnimation()
- {
- if (Mathf.Abs(_previousMovementInput.x) >= float.Epsilon)
- {
- Animator.Play("Walk");
- Animator.SetFloat("xSpeed", Mathf.Abs(_rb.velocity.x / moveSpeed));
- }
- else
- {
- if (Mathf.Approximately(Mathf.Floor(_rb.velocity.x), 0)) { Animator.Play("Idle"); }
- else { Animator.SetFloat("xSpeed", Mathf.Clamp01(Mathf.Abs(_rb.velocity.x))); }
- }
- }
- //Walk While in the air
- public void AirWalk()
- {
- var x = Mathf.SmoothDamp(_rb.velocity.x, TargetMoveSpeed.x, ref currentXVelocity, airSmoothingFactor);
- _rb.velocity = new Vector2(x, _rb.velocity.y);
- }
- public void AirResistMoveSimpler()
- {
- _rb.velocity = new Vector2(Approach(_rb.velocity.x, moveSpeed * _previousMovementInput.x, airRecoveryRate * .65f * Time.fixedDeltaTime), _rb.velocity.y);
- }
- public void Hop()
- {
- _rb.velocity = new Vector2(_rb.velocity.x, 0);
- _rb.gravityScale = startGravityScale;
- SetJumpForceAtDesiredHeight();
- var jumpForce = m_jumpForce * 1.025f / 2;
- jumpLimitTimer = MaxJumpTime;
- float xforce = _rb.velocity.x + (Mathf.Abs(_rb.velocity.x) > 0 ? Mathf.Sign(_rb.velocity.x) * Mathf.Abs(_previousMovementInput.x) : 0) * 1.025f;
- _rb.velocity = new Vector2(xforce, jumpForce);
- }
- public void FrameIndependentHeldJump(float heldTime)
- {
- //ToDo: Remove Double Guard statement
- if (heldTime <= MaxJumpTime && Vector2.Dot(_rb.velocity, Vector2.up) > 0)
- {
- float frameRatio = heldTime / MaxJumpTime;
- frameRatio = Mathf.Abs(frameRatio - 1);
- //Debug.Log(frameRatio);
- //float yForce = Mathf.Lerp(m_jumpForce + 2, 0, frameRatio);
- _rb.AddForce(m_jumpForce * 1.025f * 2 * _rb.mass * Vector2.up * frameRatio);
- }
- else { jumpInput = false; }
- }
- public void SetJumpForceAtDesiredHeight()
- {
- m_jumpForce = Mathf.Sqrt(_rb.gravityScale * Physics2D.gravity.magnitude * 2 * jumpHeight);
- }
- public void ChangeState(NecroBaseState state)
- {
- //Guard Statement
- if (state == m_currentState) { return; }
- m_currentState = state;
- m_currentState.EnterState(this);
- }
- public static float Approach(float val, float target, float maxMove)
- {
- return val > target ? Mathf.Max(val - maxMove, target) : Mathf.Min(val + maxMove, target);
- }
- public void TryToDie() { if (invincibleFrameTimer <= 0) { Death(); } }
- public void Death()
- {
- ChangeState(m_necroDeadState);
- SFXManager.instance.Play("Splat");
- }
- //ToDo: Move CapsuleMorph to another script
- public void MorphCapsuleColliderVertical()
- {
- if (_capsuleCollider.direction == CapsuleDirection2D.Vertical) { return; }
- _capsuleCollider.direction = CapsuleDirection2D.Vertical;
- _capsuleCollider.offset = Vector2.zero;
- DOTween.To(() => _capsuleCollider.size, x => _capsuleCollider.size = x, initialVerticalCapsuleSize, .45f).SetEase(Ease.InOutCubic);
- }
- public void Revive()
- {
- invincibleFrameTimer = invincibleTime;//This can also be set when we enter the death state
- ReturnToCorrectState();
- }
- public void ReturnToCorrectState()
- {
- if (_collisionHelper.onGround) { ChangeState(GroundedState); }
- else { ChangeState(FallingState); }
- }
- public void MorphCapsuleColliderHorizontal()
- {
- if (_capsuleCollider.direction == CapsuleDirection2D.Horizontal) { return; }
- _capsuleCollider.direction = CapsuleDirection2D.Horizontal;
- _capsuleCollider.offset = capsuleColliderDeadOffset;
- DOTween.To(() => _capsuleCollider.size, x => _capsuleCollider.size = x, new Vector2(initialVerticalCapsuleSize.y, initialVerticalCapsuleSize.x), .45f).SetEase(Ease.InOutCubic)/*.OnComplete(Hop)*/;
- }
- private void OnTriggerEnter2D(Collider2D collision)
- {
- TryPopulateInteractable(collision);
- }
- private void OnTriggerExit2D(Collider2D collision)
- {
- TryDisposeInteractable(collision);
- }
- private void TryPopulateInteractable(Collider2D collision)
- {
- var other = collision.GetComponent<IInteractable>();
- if (!ReferenceEquals(other, null) && ReferenceEquals(_interactable, null)) { _interactable = other; }
- }
- private void TryDisposeInteractable(Collider2D collision)
- {
- var other = collision.GetComponent<IInteractable>();
- if (!ReferenceEquals(other, _interactable)) { DisposeInteractable(); }
- }
- public void DisposeInteractable()
- {
- _interactable = null;
- }
- private void OnValidate()
- {
- if (Application.isPlaying) { return; }
- if (_collisionHelper == null) { _collisionHelper = GetComponent<CollisionHelper>(); }
- if (_rb == null) { _rb = GetComponent<Rigidbody2D>(); }
- }
- }