Newer
Older
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IFighterCallback
{
void OnFighterDeath(Fighter fighter);
}
// Generic subclass for any Fighter on the field, whether it's an Enemy or the AntiPlayer.
// Contains stats management such as health, attacks, damage, death, etc.
public abstract class Fighter : MonoBehaviour, IRoundCallback
{
[SerializeField] int baseHealth = 100;
[SerializeField, Tooltip("Attack every x seconds")] float baseAttackSpeed = 1f;
[SerializeField] int baseAttackDamage = 10;
[SerializeField] int baseArmor = 1;
public List<IFighterCallback> callbacks = new List<IFighterCallback>();
[SerializeField] protected int maxHealth = 100; // Dynamically valculated based on upgrades
[SerializeField] protected int currentHealth = 100;
protected float currentHealthPercentage { get => (float) currentHealth / maxHealth; }
protected new Rigidbody2D rigidbody;
protected new BoxCollider2D collider;
protected SpriteRenderer spriteRenderer;
protected HealthBarController healthBarController;
protected bool alive { get => currentHealth > 0; }
protected bool roundRunning = false;
protected FighterTypes fighterType;
protected string opponentTag;
protected Vector3 initalScale;
protected Vector3 initalPosition;
protected Sprite initialSprite;
protected abstract bool CanAttack();
protected abstract void Attack();
float timeSinceLastAttack = float.PositiveInfinity;
protected virtual void Awake()
{
animator = GetComponent<Animator>();
rigidbody = GetComponent<Rigidbody2D>();
collider = GetComponent<BoxCollider2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
healthBarController = GetComponentInChildren<HealthBarController>();
fighterType = FighterTypes.ENEMY;
initalPosition = transform.position;
initalScale = transform.localScale;
initialLayer = gameObject.layer;
initialSprite = spriteRenderer.sprite;
}
protected virtual void Start()
{
RoundController.instance.roundCallbacks.Add(this);
OnRoundEnd();
}
protected virtual void Update()
{
if (alive)
{
if (CanAttack())
timeSinceLastAttack += Time.deltaTime;
var timeUntilNextAttack = baseAttackSpeed - timeSinceLastAttack;
if (timeUntilNextAttack <= 0)
{
Attack();
timeSinceLastAttack = float.IsInfinity(timeUntilNextAttack) ? 0 : -timeUntilNextAttack; // To account for overshoot
}
}
else
{
timeSinceLastAttack = float.PositiveInfinity;
public virtual int DealDamage(int dmg)
if (!alive)
{
var actualDamage = Mathf.Max(0, Mathf.RoundToInt(dmg - baseArmor * GetStats().armorMultiplier));
currentHealth = Mathf.Max(currentHealth - actualDamage, 0);
if (currentHealth == 0)
{
animator.SetTrigger("Death");
callbacks.ForEach(c => c.OnFighterDeath(this));
// Disable own collision so that other Fighters can run over our dead body
gameObject.layer = LayerMask.NameToLayer("Ground-Only Collision");
healthBarController.SetHealthBarEnabled(false);
}
else
{
animator.SetTrigger("Hurt");
healthBarController.ShowHealth(currentHealthPercentage);
return actualDamage;
protected FighterStats GetStats()
{
return StatsManager.instance.fighterStats[fighterType];
}
void OnTriggerEnter2D(Collider2D other)
{
#if false
Debug.Log(string.Format("'{0}' (tag '{1}') collided with '{2}' (tag '{3}') in parent '{4}' (tag '{5}')", name, tag, other.name, other.tag, other.transform.parent ? other.transform.parent.name : null, other.transform.parent ? other.transform.parent.tag : null));
#endif
if (other.CompareTag(opponentTag))
var damageToDeal = Mathf.RoundToInt(baseAttackDamage * GetStats().damageMultiplier);
other.GetComponent<Fighter>().DealDamage(damageToDeal);
}
}
public virtual void OnRoundStart()
{
roundRunning = true;
animator.enabled = true;
collider.enabled = true;
rigidbody.bodyType = RigidbodyType2D.Dynamic;
maxHealth = currentHealth = Mathf.RoundToInt(baseHealth * GetStats().healthMultiplier);
}
public virtual void OnRoundEnd()
{
roundRunning = false;
animator.enabled = false;
timeSinceLastAttack = float.PositiveInfinity;
collider.enabled = false;
rigidbody.bodyType = RigidbodyType2D.Kinematic;
rigidbody.velocity = Vector2.zero;
rigidbody.MovePosition(initalPosition);
transform.localScale = initalScale;
gameObject.layer = initialLayer;
spriteRenderer.sprite = initialSprite;
callbacks.Clear();
healthBarController.SetHealthBarEnabled(false);