using System.Collections; using System.Collections.ObjectModel; using System.Linq; using MontanaJohns.Core; using MontanaJohns.Items; using UnityEngine; namespace MontanaJohns.Actors { [RequireComponent(typeof(Rigidbody2D))] public abstract class Actor : MonoBehaviour { [SerializeField] protected float gravityScale = 1.5f; [SerializeField] protected Stats baseStats = Stats.DefaultBaseStats(); [SerializeField] protected Transform groundCheckPoint1; [SerializeField] protected Transform groundCheckPoint2; [SerializeField] protected float invulnTime = 0f; [SerializeField] protected float hangTime; protected Rigidbody2D _rigidBody; protected SpriteRenderer _renderer; protected Animator _animator; protected Transform _transform; protected AudioManager _audio; protected Active activeItem; protected bool isGrappling; protected bool isMoving; protected Vector2? grapplePoint = null; protected LayerMask groundLayers; protected Collection items; protected int jumpCount; protected Vector2 acceleration; protected bool invuln; protected float hangCount; protected float previousInput; public Stats stats; public int health; protected virtual void Awake() { _rigidBody = GetComponent(); _transform = GetComponent(); _renderer = GetComponent(); _animator = GetComponent(); _audio = FindObjectOfType(); groundLayers = LayerMask.GetMask("Grapple", "Ground"); _rigidBody.freezeRotation = true; _rigidBody.collisionDetectionMode = CollisionDetectionMode2D.Continuous; _rigidBody.gravityScale = gravityScale; items = new(); stats = baseStats; health = stats.maxHealth; jumpCount = stats.maxJumps; } protected virtual void Start() { } protected virtual void FixedUpdate() { if (!_animator.GetBool("airborn") && !(Physics2D.OverlapCircle(groundCheckPoint1.position, 0.2f, groundLayers) || Physics2D.OverlapCircle(groundCheckPoint2.position, 0.2f, groundLayers))) { _animator.SetTrigger("fall"); StartCoroutine(Falling()); } } public virtual void Move(float input, float maxSpeedModifier = 0f) { var maxSpeed = stats.maxSpeed + maxSpeedModifier; _rigidBody.AddForce(new Vector2(input * stats.speedMultiplier, 0), ForceMode2D.Impulse); if (!isGrappling) { if (input == 0 || input < 0 && previousInput > 0 || input > 0 && previousInput < 0) { var target = new Vector2(0, _rigidBody.velocity.y); _rigidBody.velocity = Vector2.SmoothDamp(_rigidBody.velocity, target, ref acceleration, .05f); } } if (input < 0 && Mathf.Abs(_rigidBody.velocity.x) > maxSpeed) _rigidBody.velocity = new Vector2(-maxSpeed, _rigidBody.velocity.y); else if (input > 0 && Mathf.Abs(_rigidBody.velocity.x) > maxSpeed) _rigidBody.velocity = new Vector2(maxSpeed, _rigidBody.velocity.y); if (IsGrounded() && input == 0) _rigidBody.gravityScale = 0f; else _rigidBody.gravityScale = gravityScale; _animator.SetBool("moving", Mathf.Abs(_rigidBody.velocity.x) > 1); isMoving = Mathf.Abs(_rigidBody.velocity.x) > 1; if (_rigidBody.velocity.x < -0.1) { _renderer.flipX = true; for (int i = 0; i < transform.childCount; i++) { var rot = transform.GetChild(i).rotation; rot.y = 180f; transform.GetChild(i).rotation = rot; } } else if (_rigidBody.velocity.x > 0.1) { _renderer.flipX = false; for (int i = 0; i < transform.childCount; i++) { var rot = transform.GetChild(i).rotation; rot.y = 0f; transform.GetChild(i).rotation = rot; } } previousInput = input; } public virtual void MoveY(float inputX, float inputY, float doNotExceed, float maxSpeedModifier = 0f) { if(_rigidBody.position.y <= doNotExceed) { var maxSpeed = stats.maxSpeed + maxSpeedModifier; _rigidBody.AddForce(new Vector2(0, inputY * stats.speedMultiplier), ForceMode2D.Impulse); if (inputY < 0 && Mathf.Abs(_rigidBody.velocity.y) > maxSpeed) _rigidBody.velocity = new Vector2(_rigidBody.velocity.x, -maxSpeed); else if (inputY > 0 && Mathf.Abs(_rigidBody.velocity.y) > maxSpeed) _rigidBody.velocity = new Vector2(_rigidBody.velocity.x, maxSpeed); } } public virtual bool IsGrounded() { if (Physics2D.OverlapCircle(groundCheckPoint1.position, 0.2f, groundLayers) || Physics2D.OverlapCircle(groundCheckPoint2.position, 0.2f, groundLayers)) return true; return false; } public virtual void Jump() { if (isGrappling) Use(); if(IsGrounded()) { jumpCount = stats.maxJumps; hangCount = hangTime; } else { hangCount -= Time.deltaTime; } if (jumpCount > 0 && hangCount > 0f) { jumpCount--; _rigidBody.AddForce(Vector2.up * stats.jumpForce); _animator.SetTrigger("jump"); _animator.SetBool("airborn", true); StartCoroutine(Falling()); } } public virtual void Use() { Vector2? grapplePoint; if (activeItem != null) { grapplePoint = activeItem.Use(); if (grapplePoint != null) isGrappling = true; else isGrappling = false; } else grapplePoint = null; this.grapplePoint = grapplePoint; } public virtual void Cancel() { if (PauseMenu.isPaused) { PauseMenu.Resume(); } else { PauseMenu.Pause(); } } public virtual void AddItem(Item item) { items.Add(item); stats = baseStats + items.Select(i => i.stats).Sum(); } public virtual void Grapple(float xInput, float yInput, Vector2 grapplePoint) { Move(xInput); MoveY(xInput, yInput, grapplePoint.y); } public virtual void TakeDamage(int damage) { StartCoroutine(Damage(damage)); } private IEnumerator Damage(int damage) { if(!invuln) { invuln = true; health -= damage; StartCoroutine(DamageAnimation()); yield return new WaitForSeconds(invulnTime); invuln = false; } } private IEnumerator DamageAnimation() { _renderer.color = Color.red; yield return new WaitForSeconds(0.5f); _renderer.color = Color.white; } private IEnumerator Falling() { _animator.SetBool("airborn", true); bool falling = true; while (falling) { if (Physics2D.OverlapCircle(groundCheckPoint1.position, 0.2f, groundLayers) || Physics2D.OverlapCircle(groundCheckPoint2.position, 0.2f, groundLayers)) falling = false; yield return new WaitForEndOfFrame(); } _animator.SetBool("airborn", false); yield break; } } }